tag: Web; JavaScript; SVG; DOM; 动画 SVGhtml
最近在项目中遇到了「带动画 SVG 图标」与 「image」标签结合使用的场景,使用过程当中发现水仍是有点深,所以整理出来,供有类似场景的童鞋以参考。git
咱们这里有一个的带动画 SVG 文件 github
这是一个水波纹效果的 SVG,动画时长是固定的,可是咱们但愿不一样的标记动画的播放时长能够略有不一样,进而产生错落交错的感受。期待效果以下:web
其中控制动画时长的属性是写死在 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,以达到修改动画时间的目的。app
那么咱们先来看下 image 加载 SVG 文件几种的方式。dom
<img src="./assets/rings.svg">
复制代码
这种方式加载出来的 SVG 内容无法被 JS 获取到,更不要提修改属性,所以该方案 放弃。svg
这种方式中,依靠的是 image 标签支持 base64 和 Blob 类型 URL 的特性。测试
考虑能够先将 SVG 文件转换为对应的字符串,而后经过正则表达式将动画属性修改,而后传入 src 中来实现。不过这种方式须要编写复杂的正则表达式,而且字符串形式的 SVG 内容可读性较差,所以这种方式也 不是最优的。
若是能够先以 dom 形式修改 SVG 的动画属性,再将 dom 转换为字符串,最后再将字符串转换为 base64 或者 Blob 类型,就能够实现以上的需求了。
看起来这个思路靠谱,那么就按照这个方向继续探索。
获取 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();
复制代码
上文获取的 svgDom 就是可操做的 dom 节点,接下来和操做 dom 同样来操做它。
// 获取 svg 中的 animate 标签,使用 setAttribute 进行修改 dur、begin 等属性,如下代码仅为示例
// 获取 animation 节点
const ani = svgDom.children[0];
// 修改节点上的动画时长 dur 属性
ani.setAttribute('dur', Math.random() + 2 + 's');
复制代码
接来下便须要将 dom 转换成字符串:经过 XMLSerializer
将 XML 转换成 String 类型。
const svgStr = new XMLSerializer().serializeToString(svgDom);
复制代码
可是上面的 svgStr 是无法直接传给 image.src 的,须要将其转换为 image 能够解析的 base64 或者 Blob URL 类型。
这里我选择了 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}">`;
// 最后插入模板
复制代码
固然除了使用 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()