SVG 在 image 标签中的动态修改技巧-程序员宅基地

技术标签: c#  ViewUI  php  javascript  

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()

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33850890/article/details/91374560

智能推荐

详解热备份路由协议(HSRP)-程序员宅基地

文章浏览阅读238次。了解HSRP概述HSRP(热备份路由协议):是思科私有的一种技术,它确保了的那个网络边缘设备或接入链路出现故障时,用户通信能迅速并透明地恢复,以此为IP网络提供冗余性。热备份路由协议为IP网络提供容错和增强的路由选择功能。通过使用一个虚拟的IP地址和虚拟MAC地址,LAN网段上的两台或者多台路由器可以作为一台虚拟路由器对外提供服务。熟悉HSRP组成员HSRP备份有一台活跃路由..._hsrp占先圈的概念

python中数组转矩阵_python实现矩阵和array数组之间的转换-程序员宅基地

文章浏览阅读7.4k次。python 列表、矩阵、数组之间的关系矩阵可以实现多维切片,如: b = m[1:10, 3:20] 但是 多维列表却不可以首先,没见过b = m[1:10, 3:20] 这种写法,小编试了一下有错误 其次,多维列表也是可以切片的。python怎么将数组转换为矩阵?python将数组转换为矩阵,方法如下: 数组转换矩阵: A = mat(s[]) Python的定义: Python是一种面向对象..._python将数组转化为矩阵

Mybatis中使用Foreach遍历数组查询_mybatis foreach int数组-程序员宅基地

文章浏览阅读2k次,点赞4次,收藏2次。一入开发深似海,从此路人皆"对象"Mybatis中使用Foreach遍历数组查询(集合雷同)前言一、话不多说直接上图方法1.在sql层面上解决问题方法2 在代码方面解决问题总结前言我是在查询的过程中将数组作为查询条件用到了foreach in 进行查询,以及如何避免数据超过1000mybatis抛出异常。提示:以下是本篇文章正文内容,下面案例可供参考一、话不多说直接上图方法1.在sql层面上解决问题在写的时候注意重点最终执行的效果为:where(poolid in 1 or po_mybatis foreach int数组

JDK1.7和JDK1.8中HashMap为什么是线程不安全的_hashmap为什么线程不安全 jdk17-程序员宅基地

文章浏览阅读141次。JDK1.7和JDK1.8中HashMap为什么是线程不安全的_hashmap为什么线程不安全 jdk17

创新者的窘境 Teh Innovator‘s Dilemma_sustaining 创新-程序员宅基地

文章浏览阅读389次。成功的企业家需要投资错误。创新有两种类型:1、持续型创新 Sustaining Innovations:在现有产品或服务上的精进,对存量客户或商业模型的价值增益。到最后,你提供的服务会比客户实际需要的更多,过量服务=创新。2、破坏性创新 Disruptive Innovations: 这类创新提供的产品比市场现有产品的性能差,也许是低质量或者低精密度的,但是他们却更简单、更便宜、体验更友好。破坏性创新会击败强大的现存企业,并不是因为这些企业有弱点,而是因为这些企业为了建立客户黏性而选择在现有_sustaining 创新

C++使用nlohmann/json 转 C++ 结构体_josn.hpp向量转为c++结构体-程序员宅基地

文章浏览阅读1.5k次。参考网址:https://icode.best/i/33144046003522使用宏展开的方式:其中的宏定义在json.hpp中:nlohmann_json_j.value(#v1, nlohmann_json_t.v1);这里使用json.value的取值方式。_josn.hpp向量转为c++结构体

随便推点

数据结构考研笔记之栈与队列(四)栈与队列应用----括号匹配、中缀表达式转前缀后缀问题_中缀表达式转前缀题目-程序员宅基地

文章浏览阅读1.6k次。栈与队列1.括号匹配问题栈例题1例题2-----不匹配例题1例题3-----不匹配例题22. 表达式求值问题例题1.中缀表达式转前缀表达式2.中缀表达式转后缀表达式实现过程:3. 递归:递归产生的问题:1.括号匹配问题栈例题1算法思想:1)初始一个空栈,顺序读入括号。若是右括号,则与栈顶元素进行匹配·若匹配,则弹出栈顶元素并进行下一个元素·若不匹配,则该序列不合法3)若是左括号,则压入栈中4)若全部元素遍历完毕,栈中非空则序列不合法解题:1.首先1、2都是左括号,直接进栈2._中缀表达式转前缀题目

C# 文件监控,任何文件夹或文件修改后 ,如何立即触发监控_c#监控文件夹是否有文件更新-程序员宅基地

文章浏览阅读9.8k次,点赞11次,收藏46次。一、场景有时候,我们需要监控文件夹以及文件的变化,然后做出一些业务处理,这里面,最典型的例子,估计前端攻城狮老熟的,就是各种打包了。那么在C#后端,也会遇到要监控变化的情况。一个文件如何被修改,就会触发什么动作。二、C#中的文件监控可以用钩子函数来处理,这种会处理得让你抓狂,一可能是太多消息事件,你头都大,二估计你不是C/C++的程序员,对win32函数本身就不熟,找资料都要你老费劲了。那么C#中的文件监控还有什么办法呢?C#庞大的类库中,提供了FileSystemWatcher类,该类_c#监控文件夹是否有文件更新

Java(顾客最短等待时间)_java 根据人数 安排最少时间怎么写-程序员宅基地

文章浏览阅读2k次。题目如下:设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti(1&lt;i&lt;n),共有s处可以提供此项服务。应如何安排n个顾客的服务次序才能使得平均等待时间达到最小?平均等待时间是n个顾客等待时间的总和除以n。代码如下:package TXSF;import java.util.Scanner;public class ZY { public static vo..._java 根据人数 安排最少时间怎么写

C++调用函数求正整数中各个数字的个数_c++整数的个数-程序员宅基地

文章浏览阅读729次。.main函数中输入一个正整数,调用函数calc_number求出此正整数中每个数字的个数,并把结果返回main函数,并在main函数中输出结果_c++整数的个数

DNF与PXE网络装机(Linux)_dnf挂载-程序员宅基地

文章浏览阅读117次。cp -rf /mnt/* /var/ftp/dnf //用于存放客户端需要的rpm包软件,必放在此目录中,该目录为FTP共享根目录。gpgkey=ftp://192.168.147.2/dnf/RPM-GPG-KEY-openEuler //注意此处是两个斜杠。部署PXE远程安装服务。# cd /etc/yum.repos.d/ //把openeuler自带的替换成我们的DNF,注意地址。# cp -rf /mnt/* /var/ftp/dnf //拷贝一份当镜像放到我们的DNF服务器下。_dnf挂载

Linux打包python(pyinstaller)及问题解决_linux pyinstaller-程序员宅基地

文章浏览阅读994次,点赞22次,收藏20次。弄了两周,一堆办法都没有用,我混合了好几个办法弄好了。_linux pyinstaller

推荐文章

热门文章

相关标签