杂记 › 技术笔记 › SVG 预览的尺寸听谁的:显示框恒等于容器,删掉 viewBox 后 r=1 实测 2 像素

SVG 预览的尺寸听谁的:显示框恒等于容器,删掉 viewBox 后 r=1 实测 2 像素

SVG预览有两套尺寸口径:原始尺寸栏按宽高到viewBox再到300回退读数,实际显示框被强制撑满并删掉viewBox、恒等于容器;删viewBox后半径1的圆恒定2像素。

上传后「原始尺寸」和源文件写的不一样

拿一枚 <svg viewBox="0 0 24 24"> 试预览,「原始尺寸」写着 24 × 24 px,可预览框里那圈图形撑满了比它大得多的区域;换成把宽高写成 480 的,读数跟着变 480 × 480 px,框却没变。两个数各走各的——这个页面有两套尺寸口径。

「原始尺寸」这一栏只按回退链读数

这个页面是 SVG图片预览,它的 renderSvg 兵分两路。一路喂进 DOMParser 只算「原始尺寸」:先看根标签的 width、height 属性,缺位才取 viewBox 第 3、4 段,viewBox 也没有就写死 300,最后 parseFloat 取 Math.round 拼成「N × M px」。另一路把节点插进预览框,先把 width、height 改成 100%,再 removeAttribute('viewBox')。两条路数值互不传递,读数与画出来的框经常对不上。

根标签写法「原始尺寸」读数实际显示框
width=200 height=100200 × 100 px600×400
只有 viewBox 0 0 24 2424 × 24 px600×400
width=480 与 viewBox 冲突480 × 480 px600×400
宽高与 viewBox 都缺300 × 300 px600×400
只写 style width:200px height:150px300 × 300 px200×150

前四行里,源文件写 200、24 还是 480 都一样:viewBox 被删、宽高强制 100% 后,真节点外框等于预览容器——探针容器固定 600.00×400.00,实测每行都落在这个值。只有最后一行例外:宽高写进行内 style 时,200px、150px 压过铺上去的 100%,实测框才是 200.00×150.00。「原始尺寸」栏从来不是显示框尺寸,它是回退读数。

两套口径:读数走回退链,框恒等于容器 源 width=200 height=100 读数 200 × 100 框 600×400 源 viewBox 0 0 24 24 读数 24 × 24 框 600×400 源 只有行内 style 读数 300 × 300 框 200×150

图 1:「原始尺寸」读数与实际显示框的两套口径

删掉 viewBox 之后,r=1 的圆永远是 2 像素

viewBox 本来把用户坐标映射到视口,被 removeAttribute 之后,用户坐标与 CSS 像素 1:1 落地,图形多大只由自身数字说了算。同一枚 <circle r="1"> 塞进 24 单位、480 单位、冲突、什么都不写、嵌套这五种源,走 renderSvg 后直径实测全是 2.00×2.00。可若不删 viewBox、按原生方式在同一 600×400 框里渲染,r=1 在 viewBox="0 0 24 24" 下量到 33.33×33.33、在 viewBox="0 0 480 480" 下量到 1.67×1.67,原生口径下相差约 20 倍。删掉 viewBox 把这 20 倍差距拉平成同一个 2.00。

缩放每档 0.2、夹在 20% 到 500%,「适应」其实是复位

zoomSvg 每次加减 0.2,再把 svgZoom 夹住,倍率停在 20% 到 500%:从 1 起连点放大是 1.2、1.4、1.6,到 500% 后原样不动;连点缩小到 20% 也不再变。写着「适应」的按钮,fit 分支无视当前倍率、直接把 svgZoom 设回 1,即复位成 100%,不是贴合窗口。读数用 Math.round(svgZoom*100),浮点 1.5999… 显示 160%(截断会是 159%)、4.999… 显示 500%(截断会是 499%),是四舍五入不是截断。

「SVG 源码」里的缩进,其实只在开标签前插换行

源码 Tab 是 formatSourceCode 的产物,名字挂着「缩进」,实测它只在每个开标签前插一个换行再上三色高亮,不补缩进空格、不改属性顺序、不拆自闭合。那枚 121 字符的单行样本,换行阶段变 6 行、129 字符,加上转义与高亮后 innerHTML 达 950 字符。

近半兆的大文件,代码里没有任何上限守卫

一份 498635 字节、约 0.47MB、单行铺满 12000 个 rect 的大 SVG 喂进去,renderSvg 耗时 308 毫秒跑完,元素数量统计成 12001,显示框仍是 600×400。这个 12001 是正则 /<\w+/g 逐个匹配开标签样式计出的,即 1 个 svg 加 12000 个 rect,闭合标签与注释不计。代码里既无字节上限也无元素上限守卫,大文件不会被拦下。

失败路径与文案对照

失败提交都在清空预览前提前 return,旧预览原样留着。输入框为空弹「请上传 SVG 文件或粘贴代码」;丢一段没标签的文本、或一个没闭合的 <svg width="10" height="10",取不到 svg 节点弹「未找到有效的 SVG 标签」。带 BOM 的会被 trim 剥掉照常解析;GBK 字节文件端到端未实测,只能确认 readAsText 未传编码参数、默认按 UTF-8 解码,ASCII 标签仍能解析。内联脚本对照:塞进 <script> 经 innerHTML 注入后节点插入(计数 1)但不执行;<image> 只测 data: 与本地相对路径两种 href,节点存活,像素是否真正解码未据此断言,全程不指向任何外部主机。

页面说法与实测对照,config 介绍共 6 条可判定:上传预览、纯前端不上传两条一致;代码高亮、基本信息、缩放查看三条部分一致——高亮其实只有换行、尺寸是回退读数、缩放被夹在 20% 到 500%。对不上的是那句「实时渲染」:代码没有任何 input、paste、keyup 监听去触发 renderSvg,只有「渲染预览」按钮和文件 readAsText 回调两条触发,粘贴完不点按钮画面不会变。

FAQ

Q:为什么信息栏尺寸和预览框里看到的不一样?

两套数。「原始尺寸」走 width/height→viewBox→300 回退读数,显示框被强制 100% 且删了 viewBox,恒等于容器。想知道画多大看预览框本身。

Q:那个「适应」按钮能让图铺满窗口吗?

不能。fit 分支只把倍率复位成 100%,不是贴合窗口。放大每点一次加 0.2,最多到 500%。

Q:预览里的圆忽大忽小,是不是 viewBox 写错了?

不是 viewBox 写错。是预览在注入前删了 viewBox,坐标按 1:1 落地,r=1 恒定 2 像素。至于把 SVG 转成图片、外部字体或图片在这类处理里怎么被丢,是另两条话题,分别见旧文 SVG 转 PNG 的导出尺寸 与 SVG 的外部字体与外链图片。

📎 相关文章

120 个词槽、15 条整句、1 句写死的开头:lorem-ipsum 的词表清点与重复片段实测 2026-11-05 文本分列后空字段没丢:a,,b 照留、列号越界填空、填 0 或 abc 退回全部列实测 2026-11-05 13 位连写只截出 11 位:text-extract 七个内置类型的正则边界实测 2026-11-05 1 GB 文件由 1024 块拼成:任意大小文件生成的分块节奏、取消边界与三种填充字节 2026-11-04 文件名提取的三处边界:结尾斜杠只兜一格、查询串整块留下、空行静默消失 2026-11-04