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 陷阱 window全局对象
Nov 26 Javascript
HTML复选框和单选框 checkbox和radio事件介绍
Dec 12 Javascript
ExtJs中gridpanel分组后组名排序实例代码
Dec 02 Javascript
关于两个jQuery(js)特效冲突的bug的解决办法
Sep 04 Javascript
php main 与 iframe 相互通讯类(js+php同域/跨域)
Sep 14 Javascript
JavaScript实现数值自动增加动画
Dec 28 Javascript
Vue2.0子同级组件之间数据交互方法
Feb 28 Javascript
如何为vuex实现带参数的 getter和state.commit
Jan 04 Javascript
微信小程序中如何使用flyio封装网络请求
Jul 03 Javascript
webpack4 配置 ssr 环境遇到“document is not defined”
Oct 24 Javascript
JavaScript this使用方法图解
Feb 04 Javascript
js模拟实现百度搜索
Jun 28 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中extract()函数的定义和用法
2012/08/17 PHP
fetchAll()与mysql_fetch_array()的区别详解
2013/06/05 PHP
将酷狗krc歌词解析并转换为lrc歌词php源码
2014/06/20 PHP
PHP连接sql server 2005环境配置及问题解决
2014/08/08 PHP
PHP获取文件扩展名的4种方法
2015/11/24 PHP
PHP封装类似thinkphp连贯操作数据库Db类与简单应用示例
2019/05/08 PHP
Laravel相关的一些故障解决
2020/08/19 PHP
js中cookie的使用详细分析
2008/05/28 Javascript
javascript中的变量是传值还是传址的?
2010/04/19 Javascript
基于jquery实现控制经纬度显示地图与卫星
2013/05/20 Javascript
jQuery实现在最后一个元素之前插入新元素的方法
2015/07/18 Javascript
jQuery实现仿美橙互联两级导航菜单效果完整实例
2015/09/17 Javascript
jQuery插件datatables使用教程
2016/04/21 Javascript
基于JS判断iframe是否加载成功的方法(多种浏览器)
2016/05/13 Javascript
js验证框架实现代码分享
2016/05/18 Javascript
vue组件watch属性实例讲解
2017/11/07 Javascript
js单线程的本质 Event Loop解析
2019/10/29 Javascript
JS实现简单省市二级联动
2019/11/27 Javascript
python三元运算符实现方法
2013/12/17 Python
跟老齐学Python之从格式化表达式到方法
2014/09/28 Python
Python中MySQL数据迁移到MongoDB脚本的方法
2016/04/28 Python
Python变量和字符串详解
2017/04/29 Python
使用python进行文本预处理和提取特征的实例
2018/06/05 Python
对python字典过滤条件的实例详解
2019/01/22 Python
基于PyQt4和PySide实现输入对话框效果
2019/02/27 Python
numpy np.newaxis 的实用分享
2019/11/30 Python
浅谈优化Django ORM中的性能问题
2020/07/09 Python
Html5实现移动端、PC端 刮刮卡效果
2016/06/30 HTML / CSS
高品质和独特的产品世界:Creations and Collections
2018/01/07 全球购物
LN-CC美国:伦敦时尚生活的缩影
2019/02/19 全球购物
哥德堡通行证:Gothenburg Pass
2019/12/09 全球购物
英国奢侈品牌时尚购物平台:Farfetch(支持中文)
2020/02/18 全球购物
知识竞赛活动方案
2014/02/18 职场文书
经典演讲稿汇总
2014/05/19 职场文书
后天观后感
2015/06/08 职场文书
浅谈Python中对象是如何被调用的
2022/04/06 Python