动态 favicon:显示徽标或实时状态
浏览器标签页上那个红色徽标,其实只是一个被重新绘制的 favicon。下面讲讲怎么在不引入构建步骤的情况下做到这一点。
为什么不直接用 GIF?
你可以把动画 GIF 设为 favicon,但支持情况参差不齐。Firefox 会让它动起来。Safari 只显示第一帧。Chrome 的行为在各版本间也有变化。用来做装饰还行,但它是做实时徽标的错误工具,因为你没法在运行时改变 GIF 显示的内容。
要显示在标签页打开期间会更新的计数或状态,你需要用 JavaScript 重新绘制图像。canvas 给你一个真正的像素缓冲区,你可以往里绘制,再把它作为 data URI 交给 favicon 的 link。
工作原理
- 1
加载你的基础图标
把你正常的 favicon 图像画到一个小 canvas 上,通常是 32x32。当没有任何东西需要标记时,用户看到的就是这个图标。
- 2
在上面绘制徽标
有计数时,在一个角落画一个小的填充圆,并在圆内渲染数字。保持高对比度,让它在 32 像素下依然清晰可读。
- 3
替换 favicon 的 href
把 favicon link 的 href 设为 canvas 的输出。浏览器会立即用你的新图像重绘标签页。
- 4
变化时更新
每当计数变化时重新执行这套「绘制并替换」,当计数降到零时重新画回朴素图标。
替换过程,用纯 JavaScript
function setFaviconBadge(count) {
const canvas = document.createElement("canvas");
canvas.width = 32;
canvas.height = 32;
const ctx = canvas.getContext("2d");
// draw your base icon first (an <img> you preloaded)
ctx.drawImage(baseIcon, 0, 0, 32, 32);
if (count > 0) {
ctx.fillStyle = "#e5484d";
ctx.beginPath();
ctx.arc(23, 9, 9, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "#ffffff";
ctx.font = "bold 12px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(count > 9 ? "9+" : String(count), 23, 10);
}
let link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement("link");
link.rel = "icon";
document.head.appendChild(link);
}
link.href = canvas.toDataURL("image/png");
}baseIcon 是你只加载一次的 Image。每当数字变化时调用 setFaviconBadge(unread)。把显示上限设为 9+,让两位数依然清晰可辨。
常见问题
怎么在浏览器标签页里显示未读计数?
把你的图标和带数字的徽标画到一个 canvas 上,然后把 favicon link 的 href 设为 canvas.toDataURL()。每当计数变化时重新执行。普通网页并没有单独的标签页徽标 API。
我可以用动画 GIF 作为 favicon 吗?
可以,但支持并不一致。Firefox 会让它动起来,Safari 只显示第一帧,Chrome 则各版本不一。对于会更新的实时徽标,请改用 canvas 并替换 href。
动态 favicon 会影响性能吗?
不会。绘制一个 32x32 的 canvas 并设置一个 data URI 的成本很低。只要在值变化时才重绘、而不是靠定时器不停重绘,你就察觉不到任何开销。