一个 SVG 文件放进 <img> 或者交给 sharp 栅格化时,它的显示尺寸来自文件自己声明的固有尺寸 (intrinsic size),而不是你以为的"按 viewBox 自动适配"。少了 width/height,或者写了相对单位,就会静默出问题。

三种写法各自的后果

根标签写法放进 <img>(无 CSS 约束)交给 sharp 栅格化
width="24" height="24" viewBox="0 0 24 24"24×24,正常按 24×24 出图,正常
viewBox="0 0 24 24"(无 width/height)无固有尺寸,浏览器按默认对象尺寸兜底,通常塌成 0 或 300×150按 viewBox 尺寸出图(即 24×24)
width="1em" height="1em"解析成根元素字号(通常 16px),与邻居不一致同样按 16×16 出图

第二种和第三种都不报错、不 404,所以排查时最容易漏。

真实踩坑一:sharp 栅格化出全透明 PNG

生成作品集技能图标时,脚本把 simple-icons 的 path 包成 SVG:

// 有问题:只给 viewBox
const svgFromPath = (path, hex) =>
  `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#${hex}" d="${path}"/></svg>`;

然后为了留 12% 边距,又把 path 包进 <g transform="translate(30.72,30.72) scale(8.125)">——这套算法假设画布是 256×256。

但 sharp 拿到只声明 viewBox="0 0 24 24" 的 SVG 时,按 24×24 出图。translate 把图形推到 x ∈ [30.72, 225.7],整个落到 24×24 画布之外。

症状:生成 7 个图标,全部得到 104 字节、内容完全相同的全透明 PNG。页面不报错,只是图标位置空着。

修复:让根标签自己声明画布尺寸。

const svgFromPath = (path, hex) =>
  `<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256"><path fill="#${hex}" d="${path}"/></svg>`;

验证手法:不要只看文件大小,用 sharp 读 alpha 通道。

const s = await sharp(png).stats();
console.log(s.channels[3].min, s.channels[3].max); // max===0 就是全透明

真实踩坑二:lobe-icons 的 1em 写法

从 lobe-icons 那套抄来的图标长这样,是为内联 SVG(直接写进 HTML、跟随父级字号)设计的:

<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="...">

塞进 <img src="..."> 后尺寸语义就变了:1em 解析的是 SVG 文档自身的根字号(默认 16px),跟页面上的父级字号没有任何关系。结果就是这一排图标里,别的是 15px,它是 16px——视觉上不齐。

统一改成 width="24" height="24" 即可,实际显示尺寸交给 CSS 统一控制。

检查清单

  • [ ] SVG 根标签有没有 width / height 属性(不是只有 viewBox)
  • [ ] 单位是绝对单位,不是 em / %
  • [ ] 栅格化产物用 alpha max > 0 验证过,而不是只看出没出文件
  • [ ] 一批图标一起生成时,比对文件大小:大小完全相同基本就是全透明

相关


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Web