为何canvg配置中scaleWidth与scaleHeight需设为400?如何缩至40?
问题描述
我尝试将宽高约44像素的SVG渲染为40像素的尺寸,但发现必须把canvg preset中的scaleWidth和scaleHeight设为400才能接近预期效果。根据官方文档描述,这两个属性是用于将SVG分别按水平、垂直方向缩放到指定宽度和高度,我本应设置preset.scaleWidth = 40;和preset.scaleHeight = 40;,但实际却不是这样。请问该如何正确理解这些属性,以实现将SVG渲染为40像素宽高的需求?
代码示例
主页面JavaScript代码
const svg = `<svg xmlns="http://www.w3.org/2000/svg" fill="#ff0000" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 0c17.7 0 32 14.3 32 32l0 10.4c93.7 13.9 167.7 88 181.6 181.6l10.4 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-10.4 0c-13.9 93.7-88 167.7-181.6 181.6l0 10.4c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-10.4C130.3 455.7 56.3 381.7 42.4 288L32 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l10.4 0C56.3 130.3 130.3 56.3 224 42.4L224 32c0-17.7 14.3-32 32-32zM107.4 288c12.5 58.3 58.4 104.1 116.6 116.6l0-20.6c0-17.7 14.3-32 32-32s32 14.3 32 32l0 20.6c58.3-12.5 104.1-58.4 116.6-116.6L384 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l20.6 0C392.1 165.7 346.3 119.9 288 107.4l0 20.6c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-20.6C165.7 119.9 119.9 165.7 107.4 224l20.6 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-20.6 0zM256 224a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>`; var blob = new Blob( Array.prototype.map.call( document.querySelectorAll('script[type="text/js-worker"]'), function (oScript) { return oScript.textContent; } ), { type: "text/javascript" } ); const worker = new Worker(window.URL.createObjectURL(blob), { type: "module" }); const img = document.querySelector("img"); worker.postMessage({svg: svg}); worker.onmessage = (event) => { img.src = event.data.pngUrl; };
Worker脚本代码
<script type="text/js-worker"> import { DOMParser } from 'https://cdn.skypack.dev/@xmldom/xmldom@^0.7.5' import { Canvg, presets } from 'https://cdn.skypack.dev/canvg@^4.0.0' const preset = presets.offscreen({ DOMParser }) preset.ignoreClear = true; preset.scaleWidth = 400; preset.scaleHeight = 400; self.onmessage = async (event) => { const { svg } = event.data; const radius = 28; const canvas = new OffscreenCanvas( radius * 2, radius * 2 ); // width, height const ctx = canvas.getContext( '2d' ); ctx.beginPath(); ctx.arc(radius, radius, radius, 0, 2 * Math.PI); ctx.fillStyle = 'blue'; ctx.fill(); const v = await Canvg.from(ctx, svg, preset) await v.render() const blob = await canvas.convertToBlob() const pngUrl = URL.createObjectURL(blob) self.postMessage({ pngUrl }) } </script> <img />
问题分析与解决方案
正确理解scaleWidth/scaleHeight
这两个属性的作用是将SVG的viewBox所定义的逻辑内容,缩放到canvas坐标系中的指定宽高(单位为像素,与canvas实际像素尺寸一一对应)。你的SVGviewBox是0 0 512 512,意味着SVG的原始逻辑尺寸是512x512单位,设置scaleWidth=40就会把这512单位的内容缩放到40像素宽,刚好匹配你的需求。
之前设置400能接近预期是因为:你的canvas实际尺寸是56x56像素,400/512≈0.781的缩放比例,56*0.781≈44,刚好是你提到的“约44像素的SVG”,但这并非目标的40px。
实现40px尺寸的具体修改
方案1:保持现有56x56画布,居中渲染40px SVG
修改Worker脚本中的preset参数,设置正确的缩放和偏移量:
preset.scaleWidth = 40; preset.scaleHeight = 40; preset.offsetX = (56 - 40)/2; // 水平居中偏移8px preset.offsetY = (56 - 40)/2; // 垂直居中偏移8px
修改后的Worker脚本完整代码:
<script type="text/js-worker"> import { DOMParser } from 'https://cdn.skypack.dev/@xmldom/xmldom@^0.7.5' import { Canvg, presets } from 'https://cdn.skypack.dev/canvg@^4.0.0' const preset = presets.offscreen({ DOMParser }) preset.ignoreClear = true; preset.scaleWidth = 40; preset.scaleHeight = 40; preset.offsetX = 8; preset.offsetY = 8; self.onmessage = async (event) => { const { svg } = event.data; const radius = 28; const canvas = new OffscreenCanvas( radius * 2, radius * 2 ); const ctx = canvas.getContext( '2d' ); ctx.beginPath(); ctx.arc(radius, radius, radius, 0, 2 * Math.PI); ctx.fillStyle = 'blue'; ctx.fill(); const v = await Canvg.from(ctx, svg, preset) await v.render() const blob = await canvas.convertToBlob() const pngUrl = URL.createObjectURL(blob) self.postMessage({ pngUrl }) } </script>
方案2:直接使用40x40画布,SVG占满整个画布
如果不需要蓝色圆形的56px尺寸,可以直接将画布改为40x40,此时无需偏移:
// 修改画布尺寸 const canvas = new OffscreenCanvas(40, 40); // 若不需要蓝色圆形,可移除以下绘制代码 // ctx.beginPath(); ... ctx.fill();
这样设置scaleWidth=40和scaleHeight=40后,SVG会直接占满整个40px的画布,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Eric G
相关产品推荐
相关产品推荐

