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 相关文章推荐
jquery中$.post()方法的简单实例
Feb 04 Javascript
js判断undefined类型示例代码
Feb 10 Javascript
JS设置网页图片vspace和hspace属性的方法
Apr 01 Javascript
JavaScript清空数组元素的两种方法简单比较
Jul 10 Javascript
学习JavaScript设计模式(接口)
Nov 26 Javascript
Angular 中 select指令用法详解
Sep 29 Javascript
jQuery Ajax全解析
Feb 13 Javascript
javascript 缓冲运动框架的实现
Sep 29 Javascript
vue展示dicom文件医疗系统的实现代码
Aug 27 Javascript
vue中使用 pako.js 解密 gzip加密字符串的方法
Jun 10 Javascript
jQuery实现简单弹幕效果
Nov 28 jQuery
解决idea开发遇到javascript动态添加html元素时中文乱码的问题
Sep 29 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
人大复印资料处理程序_查询篇
2006/10/09 PHP
php浏览历史记录的方法
2015/03/10 PHP
firefox中JS读取XML文件
2006/12/21 Javascript
json 实例详细说明教程
2009/10/31 Javascript
js数据验证集合、js email验证、js url验证、js长度验证、js数字验证等简单封装
2010/05/15 Javascript
很棒的学习jQuery的12个网站推荐
2011/04/28 Javascript
MyEclipse取消验证Js的两种方法
2013/11/14 Javascript
javascript实现网页子页面遍历回调的方法(涉及 window.frames、递归函数、函数上下文)
2015/07/27 Javascript
全面解析Bootstrap布局组件应用
2016/02/22 Javascript
Jquery ui datepicker设置日期范围,如只能隔3天【实现代码】
2016/05/04 Javascript
JavaScript实战之带收放动画效果的导航菜单
2016/08/16 Javascript
vue通过watch对input做字数限定的方法
2017/07/13 Javascript
引入JavaScript时alert弹出框显示中文乱码问题
2017/09/16 Javascript
JavaScript数组排序reverse()和sort()方法详解
2017/12/24 Javascript
一份超级详细的Vue-cli3.0使用教程【推荐】
2018/11/15 Javascript
Python提示[Errno 32]Broken pipe导致线程crash错误解决方法
2014/11/19 Python
python中pandas.DataFrame排除特定行方法示例
2017/03/12 Python
python内置数据类型之列表操作
2018/11/12 Python
Pandas读写CSV文件的方法示例
2019/03/27 Python
如何用C代码给Python写扩展库(Cython)
2019/05/17 Python
深入浅析Python中的迭代器
2019/06/04 Python
Python基础学习之基本数据结构详解【数字、字符串、列表、元组、集合、字典】
2019/06/18 Python
python3.8 微信发送服务器监控报警消息代码实现
2019/11/05 Python
flask利用flask-wtf验证上传的文件的方法
2020/01/17 Python
Python实现分数序列求和
2020/02/25 Python
Python实现Wordcloud生成词云图的示例
2020/03/30 Python
Django限制API访问频率常用方法解析
2020/10/12 Python
纯CSS改变webkit内核浏览器的滚动条样式
2014/04/17 HTML / CSS
HTML5 video 视频标签使用介绍
2014/02/03 HTML / CSS
以工厂直接定价的传奇性能:Ben Hogan Golf
2019/01/04 全球购物
Linux如何命名文件--使用文件名时应注意
2014/05/29 面试题
在职研究生自我鉴定
2013/10/16 职场文书
毕业生医学检验求职信
2013/10/16 职场文书
汽车专业毕业生推荐信
2013/11/12 职场文书
俄罗斯十大城市人口排名,第三首都仅排第六,第二是北方首都
2022/03/20 杂记
Redis特殊数据类型HyperLogLog基数统计算法讲解
2022/06/01 Redis