vue动态渲染svg、添加点击事件的实现


Posted in Javascript onMarch 13, 2020

业务需求(vue项目中)

1.页面展示svg内容
2.监听svg内部的点击事件
3.动态改变svg内部元素的属性和值

html标签

经多次实验,用embed、img等标签改变src属性的方式,均无法实现上述全部功能(尤其是svg内部点击事件),最终采用Vue.extend()方法完整实现,代码也较为简洁,html结构如下:

<template>
 <div>
  <div id="svgTemplate"></div>
 </div>
</template>

直接将svg文件的内容复制粘贴到.vue文件里,是可以在标签内直接添加@click事件完成需求的,方式简单但会造成文件过长,本文不多陈述

实现思路

1.创建xhr对象

const xhr = new XMLHttpRequest();
this.svgUrl = ...; // svg的绝对地址,在浏览器中打开能看到的那个
xhr.open("GET", this.svgUrl, true);
xhr.send();

2.监听xhr对象(获取svg的dom -> 添加事件 -> 修改dom -> 转成虚拟dom并挂载)

xhr.addEventListener("load", () => {
  // ① 获取svg的dom
  const resXML = xhr.responseXML;
  this.svgDom = resXML.documentElement.cloneNode(true);   // console.log(this.svgDom);
  
  // ② 添加click事件
  let btn = this.svgDom.getElementById("...");
  btn.setAttribute("v-on:click", "this.handleClick()");
  // ↑↑↑ 此处注意:原生事件handleClick此时在window层,解决办法见后文

  // ③ 修改 dom
  this.svgDom.getElementById("...").childNodes[0].nodeValue = ...
  this.svgDom.getElementById("...").setAttribute("style",
     `....; fill:${this.photoResult.resultColor}; ...`);
  // ↑↑↑ 用js操作dom的语法,动态设置svg部件的属性和值
  
  // ④ 将svgDom对象转换成vue的虚拟dom,创建实例并挂载到元素上
  var oSerializer = new XMLSerializer();
  var sXML = oSerializer.serializeToString(this.svgDom);
  var Profile = Vue.extend({
    template: "<div id='svgTemplate'>" + sXML + "</div>"
  });
  new Profile().$mount("#svgTemplate");
});

3.将methods里要执行的事件绑定到window下面,供外部(刚添加的 handleClick 事件)调用

async mounted() {
  window["handleClick"] = () => {
    this.takePhoto();
  };
},
methods:{
  takePhoto(){ ... }
}

到这里就基本完成需求:动态渲染了svg、用js操作dom的语法修改svg部件的属性和值、给svg部件动态添加了事件 handleClick,最后将 takePhoto() 事件绑定给了 window 对象的 handleClick,可以放心大胆的在 takePhoto() 里写你要执行的内容了!

特殊注意

给svg的dom部件添加事件时:
1.经多次尝试,只有 setAttribute + v-on:click 写法有效
2.setAttribute 不支持 @click(非原生事件),会报语法错误
3.addEventListener 和 onclick 均会被 vue 拦截
将svgDom对象转换成vue的虚拟dom时:
1.如果报错如下

则将 import Vue from "vue" 改为 import Vue from "vue/dist/vue.esm.js"
其原因及其他解决办法本文不做探讨可自行百度。
2.vue.extend() 方法是 vue 的一个构造器,用来动态创建 vue 实例,template 组件模板只能有一个根元素
3.$mount 手动挂载到 id 为 svgTemplate的 元素上,挂载后将替换原本的dom(替换原本的 <div id="svgTemplate"></div>)。由于每次更新 svg 都要重新挂载,没有找到 dom 元素是无法挂载的,因此 template 里面最外层的 div 也要加上 id 的属性:

var Profile = Vue.extend({
   template: "<div id='svgTemplate'>" + sXML + "</div>" 
   // ↑↑↑ 最外层的 id 不能省略,否则首次渲染后找不到 #svgTemplate
});
new Profile().$mount("#svgTemplate"); 
// ↑↑↑ 原本的 #svgTemplate 将被替换成 Profile 的 template

完整代码

<template>
 <div>
  <div id="svgTemplate"></div>
 </div>
</template>
<script>
import Vue from "vue/dist/vue.esm.js";

// window.handleClick = () => {
  // 原本的 handleClick 事件是 window 的
// };

export default {
 name: "svg-drawing",
 data() {
  return {
   /* 全局 */
   svgUrl: "", // svg的url
   svgDom: null, // 获取到的svg元素
   /* svg的变量 */
   photoResult: {
    resultVal: 0, // 测试结果 - 值
    resultMsg: "未检测", // 测试结果 - 字段
    resultColor: "#dcdee2" // 测试结果 - 字段背景色
   }
  };
 },
 async mounted() {
  // 将takePhoto方法绑定到window下面,提供给外部调用
  window["handleClick"] = () => {
   this.takePhoto();
  };
 },
 created() {
  this.getSvg();
 },
 methods: {
  // 初始化svg
  getSvg() {
   /* 创建xhr对象 */
   const xhr = new XMLHttpRequest();
   this.svgUrl = this.baseUrl + "/svgs/" + "test.svg";
   xhr.open("GET", this.svgUrl, true);
   xhr.send();

   /* 监听xhr对象 */
   xhr.addEventListener("load", () => {
    /* 1. 获取 dom */
    const resXML = xhr.responseXML;
    this.svgDom = resXML.documentElement.cloneNode(true);

    /* 2.SVG对象添加click事件 */
    let btnTakePhotoDom = this.svgDom.getElementById("...");
    btnTakePhotoDom.setAttribute("v-on:click", "this.handleClick()");

    /* 3. 修改 dom */
    this.svgDom.getElementById("...").childNodes[0].nodeValue = ...;
    this.svgDom.getElementById("...").setAttribute("style",
     `....; fill:${this.photoResult.resultColor}; ...`);

    /* 4.将svgDom对象转换成vue的虚拟dom */
    var oSerializer = new XMLSerializer();
    var sXML = oSerializer.serializeToString(this.svgDom);
    var Profile = Vue.extend({
     template: "<div id='svgTemplate'>" + sXML + "</div>"
    });
    // 创建实例,并挂载到元素上
    new Profile().$mount("#svgTemplate");
   });
  },
  // 事件
  takePhoto() { ... },
 },
 beforeDestroy() {
  this.svgDom = null;
 },
 watch: {
  photoResult: {
   handler(newVal, oldVal) {
    this.getSvg();
   },
   deep: true
  }
 }
};
</script>

到此这篇关于vue动态渲染svg、添加点击事件的实现的文章就介绍到这了,更多相关vue动态渲染svg、添加点击事件内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
JavaScript 事件的一些重要说明
Oct 25 Javascript
JavaScript 空位补零实现代码
Feb 26 Javascript
JavaScript游戏之优化篇
Nov 08 Javascript
文本域中换行符的替换示例
Mar 04 Javascript
iframe实用操作锦集
Apr 22 Javascript
thinkphp实现无限分类(使用递归)
Dec 19 Javascript
非常优秀的JS图片轮播插件Swiper的用法
Jan 03 Javascript
利用vue + koa2 + mockjs模拟数据的方法教程
Nov 22 Javascript
微信小程序实现跑马灯效果完整代码(附效果图)
May 30 Javascript
解决angularJS中input标签的ng-change事件无效问题
Sep 13 Javascript
vue项目创建步骤及路由router
Jan 14 Javascript
google广告之另类js调用实现代码
Aug 22 Javascript
创建nuxt.js项目流程图解
Mar 13 #Javascript
微信小程序中的上拉、下拉菜单功能
Mar 13 #Javascript
JavaScript实现公告栏上下滚动效果
Mar 13 #Javascript
使用vue实现HTML页面生成图片的方法
Mar 12 #Javascript
JavaScript实现随机点名器
Mar 25 #Javascript
JavaScript碰撞检测原理及其实现代码
Mar 12 #Javascript
JavaScript实现多个物体同时运动
Mar 12 #Javascript
You might like
PHP实现的功能是显示8条基色色带
2006/10/09 PHP
PHP文件下载类
2006/12/06 PHP
require(),include(),require_once()和include_once()的异同
2007/01/02 PHP
php正则提取html图片(img)src地址与任意属性的方法
2017/02/08 PHP
js解析与序列化json数据(一)json.stringify()的基本用法
2013/02/01 Javascript
JavaScript动态创建link标签到head里的方法
2014/12/22 Javascript
JQuery自适应窗口大小导航菜单附源码下载
2015/09/01 Javascript
javascript中window.open在原来的窗口中打开新的窗口(不同名)
2015/11/15 Javascript
JavaScript希尔排序、快速排序、归并排序算法
2016/05/08 Javascript
在Mac OS上安装使用Node.js的项目自动化构建工具Gulp
2016/06/18 Javascript
Bootstrap组件系列之福利篇几款好用的组件(推荐)
2016/06/23 Javascript
巧用jQuery选择器提高写表单效率的方法
2016/08/19 Javascript
Bootstrap中定制LESS-颜色及导航条(推荐)
2016/11/21 Javascript
js实现图片左右滚动效果
2017/02/27 Javascript
利用JS实现简单的瀑布流加载图片效果
2017/04/22 Javascript
jquery操作ul的一些操作笔记整理(干货)
2017/08/31 jQuery
jquery学习笔记之无new构建详解
2017/12/07 jQuery
详解vue axios用post提交的数据格式
2018/08/07 Javascript
Vue监听一个数组id是否与另一个数组id相同的方法
2018/09/26 Javascript
[56:01]2018DOTA2亚洲邀请赛 3.31 小组赛 B组 Effect vs EG
2018/03/31 DOTA
使用Python脚本将文字转换为图片的实例分享
2015/08/29 Python
shelve  用来持久化任意的Python对象实例代码
2016/10/12 Python
mysql 之通过配置文件链接数据库
2017/08/12 Python
python通过nmap扫描在线设备并尝试AAA登录(实例代码)
2019/12/30 Python
pytorch加载自己的图像数据集实例
2020/07/07 Python
htnl5利用svg页面高斯模糊的方法
2018/07/20 HTML / CSS
美国最大的农村生活方式零售店:Tractor Supply Company(TSC)
2017/05/15 全球购物
捷克家具销售网站:SCONTO Nábytek
2020/01/02 全球购物
小学生十佳少年事迹材料
2014/08/20 职场文书
党章培训心得体会
2014/09/04 职场文书
党政领导班子群众路线对照检查材料
2014/10/26 职场文书
2014年学生会部门工作总结
2014/11/07 职场文书
拾金不昧感谢信范文
2015/01/21 职场文书
综合素质评价个性发展自我评价
2015/03/06 职场文书
2015年世界粮食日演讲稿
2015/03/20 职场文书
MySQL8.0升级的踩坑历险记
2021/11/01 MySQL