SVG 在 image 标签中的动态修改技巧

tag: Web; JavaScript; SVG; DOM; 动画 SVG

最近在项目中遇到了「带动画 SVG 图标」与 「image」标签结合使用的场景,使用过程中发现水还是有点深,因此整理出来,供有相似场景的童鞋以参考。

问题背景

我们这里有一个的带动画 SVG 文件

这是一个水波纹效果的 SVG,动画时长是固定的,但是我们希望不同的标记动画的播放时长可以略有不同,进而产生错落交错的感觉。期待效果如下:

其中控制动画时长的属性是写死在 SVG 文件中的(animate 标签的 dur 属性)。SVG 部分内容如下:

<circle cx="22" cy="22" r="6" stroke-opacity="0">
    <animate attributeName="r"
         begin="1.5s" dur="3s"
         values="6;22"
         calcMode="linear"
         repeatCount="indefinite" />
    <animate attributeName="stroke-opacity"
         begin="1.5s" dur="3s"
         values="1;0" calcMode="linear"
         repeatCount="indefinite" />
</circle>
复制代码

另外受限于组件要求,仅能使用 image标签进行加载 SVG。因此只能从 image.src 属性作为突破入口。

解题思路

先确定基本思路:在 SVG 被插入到 image 标签前,修改 SVG 文件中动画 animate 标签的属性 dur,以达到修改动画时间的目的。

那么我们先来看下 image 加载 SVG 文件几种的方式。

1. image 的 src 直接指定文件路径
<img src="./assets/rings.svg">
复制代码

这种方式加载出来的 SVG 内容没法被 JS 获取到,更不要提修改属性,因此该方案 放弃

2. 直接加载 base64 或者 Blob URL 字符串

这种方式中,依靠的是 image 标签支持 base64 和 Blob 类型 URL 的特性。

考虑可以先将 SVG 文件转换为对应的字符串,然后通过正则表达式将动画属性修改,然后传入 src 中来实现。不过这种方式需要编写复杂的正则表达式,并且字符串形式的 SVG 内容可读性较差,因此这种方式也 不是最优的

如果可以先以 dom 形式修改 SVG 的动画属性,再将 dom 转换为字符串,最后再将字符串转换为 base64 或者 Blob 类型,就可以实现以上的需求了。

看起来这个思路靠谱,那么就按照这个方向继续探索。

处理过程

1. 获取 dom

获取 SVG 的方式有很多,但获取方式不在本文重点,故只给出原生 JS 实现方式。

const xhr = new XMLHttpRequest();
xhr.addEventListener('load', () => {
  const resXML = xhr.responseXML;
  const svgDom = resXML.documentElement.cloneNode(true);
});
xhr.open('GET', './rings.svg');
xhr.send();
复制代码
2. 修改 dom

上文获取的 svgDom 就是可操作的 dom 节点,接下来和操作 dom 一样来操作它。

// 获取 svg 中的 animate 标签,使用 setAttribute 进行修改 dur、begin 等属性,以下代码仅为示例
// 获取 animation 节点
const ani = svgDom.children[0];
// 修改节点上的动画时长 dur 属性
ani.setAttribute('dur', Math.random() + 2 + 's');
复制代码
3. 转换 domString

接来下便需要将 dom 转换成字符串:通过 XMLSerializer 将 XML 转换成 String 类型。

const svgStr = new XMLSerializer().serializeToString(svgDom);
复制代码
4. 转换 URL

但是上面的 svgStr 是没法直接传给 image.src 的,需要将其转换为 image 可以解析的 base64 或者 Blob URL 类型。

方案 A:

这里我选择了 Blob 类型进行转换,先用 new Blob([svgStr]) 转换成 Blob 类型,再通过 URL.createObjectURL(blob) 方法将字符串转换成 Blob 类型 URL 传入。

const blob = new Blob([svgStr], {
    type: 'image/svg+xml'
});
const blobStr = URL.createObjectURL(blob);
const template = `<img src="${blobStr}">`;
// 最后插入模板
复制代码
方案 B:

当然除了使用 Blob 数据类型外,我们也可以使用window.btoa()方法将 svgStr 转换为 base64 类型传入。

const base64 = window.btoa(svgStr);
const template = `<img src="data:image/svg+xml;base64,${base64}">`
// 最后插入模板
复制代码

结论

通过「读入 SVG -> 修改属性 -> 转换 URL -> 传入 image」 这样一个流程,最终实现了我们的预期。

以上思路希望对有类似场景的同学们有所启发。 最后强调的是使用环境为 Chrome 浏览器,其他浏览器未做测试。

参考资料

XMLHttpRequest
SVG Animate
XMLSerializer
Blob
URL.createObjectURL()
Using object URLs to display images
window.btoa()

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值