如何将SVG中单个元素导出为图片并计算其相对viewBox的边界框
SVG单个元素导出为图片的实现方案
方案一:准确计算元素相对根SVG viewBox的边界框
你之前遇到变换后边界框无法包含元素的问题,是因为没有对局部边界框的四个顶点分别做矩阵变换再求最小包围盒,按以下步骤操作即可解决:
- 先获取目标元素局部坐标系下的边界框:
这个参数可以把描边、标记、滤镜等超出几何边界的内容也纳入计算范围。const localBBox = targetEl.getBBox({ fill: true, stroke: true, markers: true, clipped: false }) - 构造局部边界框的四个顶点:
(localBBox.x, localBBox.y)、(localBBox.x + localBBox.width, localBBox.y)、(localBBox.x + localBBox.width, localBBox.y + localBBox.height)、(localBBox.x, localBBox.y + localBBox.height) - 计算目标元素到根SVG的变换矩阵:
const rootSVG = document.querySelector('svg') const rootScreenCTM = rootSVG.getScreenCTM() const targetScreenCTM = targetEl.getScreenCTM() // 元素局部坐标转根SVG坐标的矩阵 const toRootMatrix = targetScreenCTM.multiply(rootScreenCTM.inverse()) - 用变换矩阵分别转换四个顶点,得到根SVG坐标系下的四个坐标点,取所有点的最小x、最小y、最大x、最大y,最终的边界框为:
把这个值设置为根SVG的viewBox即可让目标元素刚好铺满视口,旋转、斜切等任意变换场景下都不会出现内容截断。const viewBoxX = minX const viewBoxY = minY const viewBoxWidth = maxX - minX const viewBoxHeight = maxY - minY
方案二:绕开边界框计算,直接导出目标元素
如果不想处理矩阵运算,还有更稳妥的实现方案,完全不会出现<use>等元素失效的问题:
- 完整克隆原SVG的根节点,保留所有
<defs>内的定义(渐变、滤镜、symbol等所有依赖资源) - 遍历克隆后的SVG节点,仅保留目标元素、目标元素的所有父级节点(保留父节点的所有变换属性)、以及
<defs>节点,删除其他所有无关节点 - 给克隆后的SVG设置和原SVG完全一致的宽高属性,此时克隆SVG里只有目标元素可见,渲染效果和原SVG中的目标元素完全一致
- 直接将克隆后的SVG转成Blob,再绘制到canvas导出为图片即可
注意事项
- 如果原SVG引用了外部字体、图片资源,需要提前将资源转成base64内联到SVG中,避免导出时资源丢失
- 如果原SVG的样式依赖外部CSS文件,需要将相关样式内联到克隆SVG的
<style>标签中
内容的提问来源于stack exchange,提问作者Helloer
相关产品推荐
相关产品推荐

