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 document.referrer 用法
Apr 30 Javascript
Jquery实现搜索框提示功能示例代码
Aug 13 Javascript
jquery取消选择select下拉框示例代码
Feb 22 Javascript
JavaScript中统计Textarea字数并提示还能输入的字符
Jun 10 Javascript
javascript event在FF和IE的兼容传参心得(绝对好用)
Jul 10 Javascript
纯JS打造网页中checkbox和radio的美化效果
Oct 13 Javascript
Node.js学习之地址解析模块URL的使用详解
Sep 28 Javascript
vue.js内置组件之keep-alive组件使用
Jul 10 Javascript
解决jquery的ajax调取后端数据成功却渲染失败的问题
Aug 08 jQuery
vue props传值失败 输出undefined的解决方法
Sep 11 Javascript
vue 列表页跳转详情页获取id以及详情页通过id获取数据
Mar 27 Javascript
js实现图片无缝循环轮播
Oct 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与jquery设置和读取cookies
2013/08/08 PHP
php通过session防url攻击方法
2014/12/10 PHP
php中get_object_vars()方法用法实例
2015/02/08 PHP
php实现的SSO单点登录系统接入功能示例分析
2016/10/12 PHP
PHP上传Excel文件导入数据到MySQL数据库示例
2016/10/25 PHP
laravel 解决路由除了根目录其他都404的问题
2019/10/18 PHP
JS维吉尼亚密码算法实现代码
2010/11/09 Javascript
$.ajax返回的JSON无法执行success的解决方法
2011/09/09 Javascript
jquery 年会抽奖程序
2011/12/22 Javascript
用javascript模仿ie的自动完成类似自动完成功的表单
2012/12/12 Javascript
javascript结合html5 canvas实现(可调画笔颜色/粗细/橡皮)的涂鸦板
2013/04/27 Javascript
js控制div弹出层实现方法
2015/05/11 Javascript
JavaScript检测上传文件大小的方法
2015/07/22 Javascript
解决前端跨域问题方案汇总
2016/11/20 Javascript
jQuery+ThinkPHP+Ajax实现即时消息提醒功能实例代码
2017/03/21 jQuery
discuz表情的JS提取方法分析
2017/03/22 Javascript
利用JavaScript如何查询某个值是否数组内
2017/07/30 Javascript
微信小程序使用component自定义toast弹窗效果
2018/11/27 Javascript
「中高级前端面试」JavaScript手写代码无敌秘籍(推荐)
2019/04/08 Javascript
详解微信小程序调用支付接口支付
2019/04/28 Javascript
[01:05:32]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#1COL VS Alliance第一局
2016/03/04 DOTA
python列表去重的二种方法
2014/02/14 Python
使用python对多个txt文件中的数据进行筛选的方法
2019/07/10 Python
python文件处理fileinput使用方法详解
2020/01/02 Python
python 使用递归回溯完美解决八皇后的问题
2020/02/26 Python
2020最新pycharm汉化安装(python工程狮亲测有效)
2020/04/26 Python
Python实现树莓派摄像头持续录像并传送到主机的步骤
2020/11/30 Python
css3实现二维码扫描特效的示例
2020/10/29 HTML / CSS
国际领先的在线时尚服装和配饰店:DressLily
2019/03/03 全球购物
迪士尼西班牙官方网上商店:ShopDisney西班牙
2020/02/02 全球购物
服务标兵事迹材料
2014/05/04 职场文书
2014年大学生社会实践自我鉴定
2014/09/26 职场文书
老人再婚离婚协议书范本
2014/10/27 职场文书
2016年社区综治宣传月活动总结
2016/03/16 职场文书
演讲稿之开卷有益
2019/08/07 职场文书
5个实用的JavaScript新特性
2022/06/16 Javascript