Favicon & App Icon Tools检测创建
基础
设计

动态 favicon:显示徽标或实时状态

· 6 min read

浏览器标签页上那个红色徽标,其实只是一个被重新绘制的 favicon。下面讲讲怎么在不引入构建步骤的情况下做到这一点。

为什么不直接用 GIF?

你可以把动画 GIF 设为 favicon,但支持情况参差不齐。Firefox 会让它动起来。Safari 只显示第一帧。Chrome 的行为在各版本间也有变化。用来做装饰还行,但它是做实时徽标的错误工具,因为你没法在运行时改变 GIF 显示的内容。

要显示在标签页打开期间会更新的计数或状态,你需要用 JavaScript 重新绘制图像。canvas 给你一个真正的像素缓冲区,你可以往里绘制,再把它作为 data URI 交给 favicon 的 link。

工作原理

  1. 1

    加载你的基础图标

    把你正常的 favicon 图像画到一个小 canvas 上,通常是 32x32。当没有任何东西需要标记时,用户看到的就是这个图标。

  2. 2

    在上面绘制徽标

    有计数时,在一个角落画一个小的填充圆,并在圆内渲染数字。保持高对比度,让它在 32 像素下依然清晰可读。

  3. 3

    替换 favicon 的 href

    把 favicon link 的 href 设为 canvas 的输出。浏览器会立即用你的新图像重绘标签页。

  4. 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 的成本很低。只要在值变化时才重绘、而不是靠定时器不停重绘,你就察觉不到任何开销。

Keep reading