黑化你的 favicon - 打造完美站点图标

教程

自黑暗模式(dark mode)流行以来,各个移动 APP 趋之若鹜,个人博客等网站也纷纷加入黑化大军。但留意下就会发现,鲜有博客让自己的 favicon 也适配了黑暗模式,部分黑色背景图标在黑暗模式下格外突兀。favicon 虽小,却是所有浏览器随时都会顶在头上的名片,黑乎乎一坨显示在那里实在有失身份(如果你的 favicon 颜色鲜明自然不会有这个问题)。

在注意到 GitHub 的 favicon 适配黑暗模式后,我的好奇心再次被勾起 - 研究一番后发现 favicon 也是个深坑,而且中文内容稀少。这里只能进行简单梳理。

什么是 favicon

简单地讲就是浏览器标签页或收藏夹里显示的这些图标。

https://cdn.jsdelivr.net/npm/dukewill@0.0.22/images/2.png

具体概念我们直接看维基百科就好:

Favicon 是 favorites icon 的缩写,亦被称为 website icon(网页图标),page icon(页面图标)或 urlicon(URL 图标)。Favicon 是与某个网站或网页相关联的图标。网站设计者可以多种方式创建这种图标,而目前也有很多网页浏览器支持此功能。浏览器可以将 favicon 显示于浏览器的地址栏中,也可置于书签列表的网站名前,还可以放在标签式浏览界面中的页标题前。

怎样设置

兼容性最好的方法是直接在站点根目录(root)下放置一个 favicon.ico 文件 - 这个很多人都知道,很多人不知道的是,这个 icon 文件甚至不需要是 .ico 格式,只要命名为 favicon.ico 即可。

主流浏览器(IE ==!)在发现没有声明 link rel="icon" 时便会从根目录搜索 favicon.ico,所以请在根目录放上这样一个文件。

这样就够了?

这样已经可以满足“显示站点图标”这一最简单的需求,而且无需额外配置,但如果仅仅这样的话,也没有写这篇文章的必要了。仅仅在根目录放置 favicon.ico 主要有这几个“麻烦”:

  1. 它会在页面加载完成后最后加载,而在 <head> 中声明则会首先加载;
  2. 若多站点共用一个根目录,则同一个 favicon.ico 可能难以满足个性化需要,有时甚至需要不同页面显示不同 favicon;
  3. 最后,越来越丰富的应用需要更多不同规格和设计的 icon 满足各种场景。

    比如 iOS 的 Safari 浏览器有一个很实用的添加到主屏幕功能(Android Chrome 或新 Edge 也有),如果不正确声明 icon,就无法生成合理的桌面图标。下图左边两个就是未正确配置的结果。

    https://cdn.jsdelivr.net/npm/dukewill@0.0.22/images/3.png

所以,在<head>中声明 icon 变得极为必要。favicon 也不再局限于 .ico 格式,可以是 .png,.svg 甚至 .gif 等。

如何声明

完整的 favicon 系统需要众多规格尺寸的文件,感兴趣的同学可以直接看这里。(实际上这个链接里的网站可以帮你一键生成几乎所有的 favicon)

但实践中我们似乎并无必要配置如此众多的 favicon,这里挑几个比较常见的供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<link rel="icon" href="/favicon.ico">
<link rel="shortcut icon" href="/favicon.ico">
// IE 10 以及更低版本,更好的方法是直接在根目录放置 favicon.ico 文件

<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
// 主流浏览器都会使用该尺寸?

<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
// 经典 favicon

<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
// iOS 浏览器,生成主屏幕图标

<link rel="manifest" href="/site.webmanifest">
// Android Chrome 或新 Edge,“添加到主屏幕”图标

<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#00000">
// MacOS Safari,固定标签页功能

<meta name="msapplication-TileColor" content="blue">
// Windows 8,8.1 和 10 开始菜单

吐槽下 Safari 蛋疼的缓存机制,给调试 mask-icon 增加不少麻烦。

Apple 对 pinned tab icon 有诸多限制,比如要求单图层,viewBox “0 0 16 16” 等,但不严格遵守这些限制似乎也可以正常使用,个人没有深入研究。

参考:Creating Pinned Tab Icons

https://cdn.jsdelivr.net/npm/dukewill@0.0.22/images/4.png

更多关于 favicon 的内容我就不敢讲了,感兴趣的请看这些介绍:

进入正题 - 黑化

两种方法,推荐更加灵活的 JS 法。

修改 .svg 样式

首先你的 favicon 必须是 .svg 格式,不然无从谈起,你可以通过 Adobe Illustrator 或一些在线转换服务获得这样的文件。你的声明看起来可以是这样:

1
<link rel="icon" type="image/svg" sizes="32x32" href="/favicon-32x32.svg">

接着只要修改这个 .svg 文件,在它的 <style> 中添加 prefers-color-scheme: dark 属性即可。浏览器会自动根据系统设置调用对应样式。

完整的 .svg 差不多是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
<svg width="32px" height="32px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72">
<defs><style>
.cls-1{
fill:#000000
}
@media (prefers-color-scheme: dark) { //黑暗模式调用该样式
.cls-1{
fill:#fff
}
}
</style></defs>
<g><circle class="cls-1" cx="913.46" cy="913.46" r="913.46"/></g><g><polygon class="cls-2" points="1 2 3 4 5 6 7"/></g> // 这里只是举例
</svg>

这个方法本身并不复杂,但实际使用中的两个主要限制是:

  1. 只能使用 .svg 格式的图标;
  2. 无法实时刷新。黑暗和亮色切换后,必须刷新页面才能看到效果,暂不清楚不同浏览器缓存对此有多大影响。

普通访问者如果不特意测试,应该不会感知到“无法实时刷新”,但自己知道就很难受。

所以就有了另一种”实时“的方法

通过 JS 控制

这也是 Github 使用的方法。通过 JS 替换声明中 href 路径。

假设你的声明如下:

1
<link rel="icon" type="image/png" sizes="32x32" href="/favicon.png">

请准备亮色和黑暗两种 favicon。JS 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
handleDarkmode(darkModeMediaQuery);
function handleDarkmode(e) {
var darkModeOn = e.matches;
var favicon = document.querySelector('link[rel="icon"]'); // 获取声明中含 rel="icon" 的元素,你可以根据需要添加更多
if (!favicon) {
return;
}
if (darkModeOn) {
favicon.href = '/favicon-dark.png'; //黑暗模式下的 favicon
} else {
favicon.href = '/favicon.png'; //其它时候的 favicon
}
}
darkModeMediaQuery.addEventListener('change', handleDarkmode);

代码参考:Updating your website favicon dynamically with dark mode

addListener 这个参数已经过时,这里已经改为最新的addEventListener,在 body 中引入以上 JS 即可。

这里有个坑需要注意,如果你同时声明了其它 link rel="icon",比如 16x16 的那个,请在声明中将尺寸为 32x32 的放在最前,否则浏览器调用的是 32x32 的 icon,而 JS 脚本修改的将是它读到的第一个 link rel=”icon” 声明。

好了,现在你拥有一个可以实时切换黑暗模式的 favicon 了。

https://cdn.jsdelivr.net/npm/dukewill@0.0.22/images/5.gif

最后,欢迎来 Telegram 交流:https://t.me/daiseax

Have fun!

黑化你的 favicon - 打造完美站点图标
本文作者:
Dukewill
发布于
2020-12-26
许可协议
转载或引用本文时请遵守许可协议,注明出处,不得用于商业用途!

评论

您所在的地区可能无法访问 Disqus 评论系统,请切换网络环境再尝试。