如何創建一個自動改變的 favicon

前幾天我發現了一個免費又好用的 Favicon 製作工具 [1],與其他 Favicon 生成器不同的是,它可以讓你用一個字符或 emoji 從零開始創建 Favicon。我很好奇地看過它的代碼後,就有了一些不同角度的思考。

結合之前閱讀的材料,讓我覺得更改網站 Favicon 是可以做到的。實際上已經有一些網站通過這種動態更新的方式給用戶發送通知:當圖標變爲紅點或者其他指示性東西的時候,表示頁面上正在發生或已經發生的變化。

剛開始我在 emojipedia.org[2] 這個網站裏來獲取靈感,這時候突然有這樣的一個想法:爲什麼不用時鐘圖標(🕛)和其他相關的圖標來顯示時間呢?每分鐘檢查一次,然後用對應的圖標去表示當前的時間。

在這篇文章中我們就會用 JavaScript 來實現這一功能。因爲我經常使用 Gatsby(一款基於 React 構建的靜態站點生成器),所以我們也會在 React 中去做演示。其實不管你要用 Favicon 做什麼,怎麼做,這些想法都是有利於你之後去解決問題的。

下面這個函數,就是以 emoji 作爲參數,返回一個有效的圖像或(圖標)的 data url:

1// Thanks to https://formito.com/tools/favicon
2
3const faviconHref = emoji =>
4  `data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22256%22 height=%22256%22 viewBox=%220 0 100 100%22><text x=%2250%%22 y=%2250%%22 dominant-baseline=%22central%22 text-anchor=%22middle%22 font-size=%2280%22>${emoji}</text></svg>`
5
6

這個函數,針對<head>中的<link>標籤加載圖標,js 改變<link>標籤中 herf 值實現改變 emoji 的功能:

 1const changeFavicon = emoji => {
 2  // Ensure we have access to the document, i.e. we are in the browser.
 3  if (typeof window === 'undefined') return
 4
 5  const link =
 6    window.document.querySelector("link[rel*='icon']") ||
 7    window.document.createElement("link")
 8  link.type = "image/svg+xml"
 9  link.rel = "shortcut icon"
10  link.href = faviconHref(emoji)
11
12  window.document.getElementsByTagName("head")[0].appendChild(link)
13}
14
15

(也可以去 StackOverflow [3] 上看看這篇關於更改網站 Favicon 的回答)。

你也可以嘗試運行一下這段代碼,在你的開發工具中複製粘貼這兩個函數,然後調用 changeFavicon(“💃") 函數. 正確運行代碼之後你就可以看到這個圖標變成這個跳舞的小圖標。

再回到我們這個時間的項目上,如果我們想要在對應的時間裏顯示正確的時間圖標,就需要根據當前時間來確定。例如,如果是 10:00,就要顯示🕙。如果是 4:30,就要顯示🕟。並不是每分鐘都需要顯示圖標,在一定的時間範圍內顯示合適的圖標就行。例如,9:45 到 10:14 之間可以顯示 10:00 的圖標;10:15 到 10:44 之前可以顯示 10:30 的圖標,等等。

可以用下面這個函數來實現:

 1const currentEmoji = () => {
 2  // Add 15 minutes and round down to closest half hour
 3  const time = new Date(Date.now() + 15 * 60 * 1000)
 4
 5  const hours = time.getHours() % 12
 6  const minutes = time.getMinutes() < 30 ? 0 : 30
 7
 8  return {
 9    "0.0": "🕛",
10    "0.30": "🕧",
11    "1.0": "🕐",
12    "1.30": "🕜",
13    "2.0": "🕑",
14    "2.30": "🕝",
15    "3.0": "🕒",
16    "3.30": "🕞",
17    "4.0": "🕓",
18    "4.30": "🕟",
19    "5.0": "🕔",
20    "5.30": "🕠",
21    "6.0": "🕕",
22    "6.30": "🕡",
23    "7.0": "🕖",
24    "7.30": "🕢",
25    "8.0": "🕗",
26    "8.30": "🕣",
27    "9.0": "🕘",
28    "9.30": "🕤",
29    "10.0": "🕙",
30    "10.30": "🕥",
31    "11.0": "🕚",
32    "11.30": "🕦",
33  }[`${hours}.${minutes}`]
34}`enter code here`
35
36

現在我們只需要每隔一分鐘調用一次 changeFavicon(currentEmoji()),如果要用 JavaScript 來實現的話,定時器 setInterval 就可以解決:

 1// One minute
 2const delay = 60 * 1000
 3
 4// Change the favicon when the page gets loaded...
 5const emoji = currentEmoji()
 6changeFavicon(emoji)
 7
 8// ... and update it every minute
 9setInterval(() => {
10  const emoji = currentEmoji()
11  changeFavicon(emoji)
12}, delay)
13
14

React 部分

因爲我的博客是基於 Gatsby 的,所以我也希望能在 React 中去使用這些代碼,並儘量減少改動。但因爲在 React Hooks 中 setInterval 會出現一些問題,而我們又需要每分鐘去調用一次,那麼該如何去解決呢?

可以先去了解一下 Dan Abramov 並看看他的這篇關於在 React Hooks 中聲明 setInterval 的文章 [4]。Dan Abramov 是一個很棒的博客作者,他習慣用簡單清晰的方式去解釋複雜的事情,如果你想要更好的理解 React Hooks 的話,強烈建議你去學習一下這篇文章,不一定要了解它的所有內容——因爲鉤子函數的優點之一就是,即使你不完全掌握它的底層原理,也可以去實現想要的功能。但重要的是要知道如何使用。下面的代碼就簡單演示了該如何使用:

 1import { useEffect } from "react"
 2import useInterval from "./useInterval"
 3
 4const delay = 60 * 1000
 5
 6const useTimeFavicon = () => {
 7  // Change the favicon when the component gets mounted...
 8  useEffect(() => {
 9    const emoji = currentEmoji()
10    changeFavicon(emoji)
11  }, [])
12
13  // ... and update it every minute
14  useInterval(() => {
15    const emoji = currentEmoji()
16    changeFavicon(emoji)
17  }, delay)
18}
19
20

最後,只需調用 useTimeFavicon() 就可以了,如果想要看它是如何運行的,這是一個已部署好的自動更改時間 favicon 的項目 [5],可以隨着時間的變化觀察它的 favicon 變化,需要進一步瞭解的話可以看一下它的項目代碼 [6]。

總結

我們將這三個不同的代碼結合起來就能實現我們想要的結果。古羅馬人講,分而治之。如果你把任務看作一個整體,你可能就會有這樣的顧慮:“怎樣才能在我的 React 開發的網站中去用合適的 favicon 顯示當前的時間並能實時更新”?完全做好並完善這些問題的細節並不是一件容易的事。

但好在於你不需要同時處理這些問題:把一個問題分成多個子問題會更有效,假如其中任何一個問題已經被其他人解決了,那就會更好的提高效率。

聽起來我們好像只是單純的在做網站開發?但其實重點是我們要知道只要明智合理的運用別人的優秀代碼是沒有錯的,對我們的學習是有幫助的。正如人們所說: 沒有必要重新發明輪子。所以不管是顯示通知,時間更新還是你能想到的其他問題,我們所學到的這些對我們去開發網站都是有提升的。

參考資料

[1]

Favicon 製作工具: https://formito.com/tools/favicon

[2]

emojipedia.org: https://emojipedia.org/

[3]

StackOverflow : https://stackoverflow.com/questions/260857/changing-website-favicon-dynamically/260876#260876

[4]

文章: https://overreacted.io/making-setinterval-declarative-with-react-hooks/

[5]

自動更改時間 favicon 的項目: https://000564499.deployed.codepen.website/

[6]

項目代碼: https://codepen.io/chriscoyier/project/editor/AzEJwv

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/sqxKDQZddxeeJoI3xZkfRw