你上传了一张极其清晰的 1024×1024 logo。生成器输出一个 16×16 favicon。而浏览器标签页中的结果是一团无法辨认的污迹。如果这听起来很熟悉,问题通常不在于你的 logo——而在于图标是如何被缩小的。以下是模糊 favicon 出现的计算机图形学原因,以及能修复它的缩放技术。
为什么单步缩放会毁掉小图标
大多数 favicon 生成器(以及大多数复制粘贴的 canvas 代码)是这样做的:
ctx.drawImage(img, 0, 0, 16, 16) // 1024px source → 16px target, one hop一次 drawImage 调用将图像缩小 64 倍时使用的是简单的双线性插值。对于 256 个输出像素中的每一个,浏览器只采样源图像中一个极小的邻域——这意味着大约 99.98% 的输入像素在被平均之前就被丢弃了。细小的笔画、字形和 1px 间隙等精细细节没有被"混合缩小",而是在随机位置被采样,这就是为什么文字变成糊状、logo 出现泥泞色块的原因。
这是一个经典的图形学问题,也有已知的解决方案: 分步缩小,每次减半(1024 → 512 → 256 → 128 → 64 → 32 → 16)。每个 2× 的步长足够小,双线性采样可以平均一个有意义的邻域,因此细节被逐渐合并而不是被跳过。这与 3D 渲染中的 mipmap 以及 canvas 中的 imageSmoothingQuality: "high" 是同一个思路——但显式地串联减半步骤可以在各浏览器中获得一致的结果。
修复方法:在 canvas 中逐步减半
function drawDownscaled(ctx, img, targetW, targetH) {
let source = img
let w = img.naturalWidth
let h = img.naturalHeight
// Halve repeatedly while both dimensions stay above the target
while (Math.floor(w / 2) >= targetW && Math.floor(h / 2) >= targetH) {
const step = document.createElement('canvas')
step.width = Math.floor(w / 2)
step.height = Math.floor(h / 2)
const stepCtx = step.getContext('2d')
stepCtx.imageSmoothingEnabled = true
stepCtx.imageSmoothingQuality = 'high'
stepCtx.drawImage(source, 0, 0, w, h, 0, 0, step.width, step.height)
source = step
w = step.width
h = step.height
}
// Final hop is now at most ~2x, which bilinear handles well
ctx.imageSmoothingQuality = 'high'
ctx.drawImage(source, 0, 0, w, h, 0, 0, targetW, targetH)
}该循环保证最后一次缩小在每个轴上都不超过约 2×——这正是插值真正有效的范围。对于一个细节丰富的 logo,效果是天壤之别:笔画保持连通,字母的计数器(字腔)得以保留,边缘保留对比度而不是溶成灰色。
即使是完美的缩放也无法挽救糟糕的源图
两个源图侧的错误造成了大多数剩余的模糊:
- 从已经很小的位图开始。64×64 的源图放大到 512 再缩小回来毫无益处——始终从 logo 的最大版本开始,更好的是矢量原稿。
- 为 512 设计并指望它在 16 下可读。出色的标签页图标是为 16px 专门设计的:去掉精细细节,将笔画加粗到至少画布的 1/16,并使用一两种颜色。如果你的 logo 包含文字标识,在 favicon 尺寸下只使用标志或首字母。
如果你想完全跳过位图缩放, SVG favicon 是分辨率无关的——浏览器以精确的 16px 渲染矢量,不涉及任何插值。
如何验证你的 favicon 是否真正清晰
- 下载生成的
favicon-16x16.png,在图像查看器中以 400% 缩放打开。你要的是轮廓分明的边缘,而不是灰色的糊。 - 将它与眯眼远看时的源 logo 并排比较——轮廓应该仍然吻合。
- 在真实的浏览器标签页中检查,包括浅色和深色主题,并与其他标签页对比,看它是否可辨识。
我们在自己的生成器中修复了这个问题
当我们针对这种失效模式审计自己的流水线时,我们在所有输出尺寸上将单步缩放替换为逐步减半(外加 imageSmoothingQuality: "high")。这就是为什么在 Favicon.one 生成的图标在其他工具涂抹成一团时仍能在 16px 下站得住脚。如果你在比较生成器,可以这样做测试:上传一个带有细笔画和小文字的细节丰富 logo,首先查看 16×16 的输出。
更多关于流水线的内容:如何正确 将 PNG 转换为 favicon,以及 favicon 尺寸指南中你需要的每一种尺寸。