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

业务需求(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、添加点击事件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-03-10

vue element 中的table动态渲染实现(动态表头)

通过在vue中使用element的table表格,实现数据动态渲染,并且动态渲染表头.通过在父组件中引入子组件表格,然后向子组件传递表格数据和表头数据. 子组件table中template模板 <el-table :data="this.tableData" height="400px" max-height="400px" size="small" row-class-name="row" cell

vue绑定的点击事件阻止冒泡的实例

当我们在使用vue做项目时,经常用到点击事件的绑定,但是我们绑在一个div上,里面的其他按钮(如删除.修改)等按钮也会加载这儿div的点击事件,而事实我们不需要,如何解决: 首先我们来区分事件冒泡.事件捕获是什么 (1)冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发. IE 6.0: div -> body -> html -> document Mozilla 1.0: div -> body -> html -> doc

vue+iview动态渲染表格详解

本文实例为大家分享了vue+iview 动态渲染表格(iview插件table),供大家参考,具体内容如下 效果图 (表格头部和表格主体都是动态渲染) 重要代码 <template> <Table ref="selection" v-for="(item) in entities" :columns="item.columns" :data="item.data" :border="false&qu

vuejs响应用户事件(如点击事件)

需求: 页面上的列表原先有3个,我们想点击一次添加一条记录,也可以在头和尾删除数据: 我们也可以删除我们想删除的任意一行记录: 如果是用传统的jquery操作,页面中js会特别多,而且操作也很麻烦. 这里用vue.js就非常简单. 我们先看页面效果: 页面初始化.png 末尾增加一项.png 删除项.png 再来看代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT

vue如何获取点击事件源的方法

整理文档,搜刮出一个vue如何获取点击事件源的方法,稍微整理精简一下做下分享. <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <title>vue click事件获取当前元素对象</title> <script src="http://cdnjs.cloudflare.com/ajax/libs

vue element动态渲染、移除表单并添加验证的实现

又接到新需求了吧~~ 背景 在一个大表单里,有可能会出现这种需求,用户可以自己操作动态添加.移除表单,更加个性化的效果. 常见于填写个人信息.附加内容的表单 例如: "工作经历"可以用户自己点击继续添加按钮,在原有的表单后面 append 多一个表单,不需要就点击右上方 X 按钮移除 问题 在实现之前,提出几个问题 vue 怎么动态渲染或移除表单上去 v-model 怎么绑定动态添加表单的 value 值 动态新增的表单如何验证 动态表单怎么填写对应的 prop ... 好吧,我当时也

Vue v-for循环之@click点击事件获取元素示例

应用背景 浏览论坛的贴子(数据库的数据通过vue遍历在html页面上) 点击帖子的标题.图片,可以查看详细的帖子(点击事件获取id) 1.设置一个隐藏值的放置Id的div,点击div就获取id,传参到后台(点击一部分) <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Vue 测试实例 - 菜鸟教程(runoob.com)</title> <

Vue+ElementUI实现表单动态渲染、可视化配置的方法

动态渲染就是有一个异步的数据,大概长这样: { "inline": true, "labelPosition": "right", "labelWidth": "", "size": "small", "statusIcon": true, "formItemList": [ { "type": "

vue通过点击事件读取音频文件的方法

我们项目现在有这样一个需求,通过select元素来选择音频文件的名称,点击按钮可以进行试听. 话不多说,直接上代码: HTML: <div class="testVoice"> <p class="p-title">提示声音:</p> <el-select v-model="valueOpt2" placeholder="请选择"> <el-option v-for=&qu

Vue中img的src是动态渲染时不显示的解决

今天在项目中遇到一个需求,设计稿如下 就是展示用户头像,数据从后端获取,要是没有拿到则显示默认图片. 项目采用vue开发,本人也是第一次在实际项目中使用vue. 自然而然采用条件渲染,我的代码如下: 保存运行查看,发现没显示默认的图片,审查元素发现图片没有被打包 有点懵~ 后来查阅资料发现图片使用require引入即可,更改后代码为: 搞定~ 以上这篇Vue中img的src是动态渲染时不显示的解决就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

详解VUE Element-UI多级菜单动态渲染的组件

以下是组件代码: <template> <div class="navMenu"> <label v-for="navMenu in navMenus"> <el-menu-item v-if="navMenu.childs==null&&navMenu.entity&&navMenu.entity.state==='ENABLE'" :key="navMenu.

vue导航栏部分的动态渲染实例

根据公司项目的需求,使用的是element-ui的nav-menu组件实现动态渲染左侧导航条的功能,这里我只写到了四级菜单. 代码部分: <el-menu class='el-menu-vertical-demo' :router='true' v-for='(item, index) in navMenu' :key='index'> <el-submenu v-if='item.childs' :index='item.name' :route = 'item.value' clas

写一个移动端惯性滑动&回弹Vue导航栏组件 ly-tab

前段时间写了一个移动端的自适应滑动Vue导航栏组件,觉得有一定实用性,大家可能会用得到(当然有些大佬自己写得更好的话就没必要啦),于是前两天整理了一下,目前已经发布到npm和GitHub上了,点我到npm,点我到GitHub项目 ,有需要的同学可以在项目中 npm install ly-tab -S 或者 yarn add ly-tab 使用,具体用法下面会讲到. 好了,先看看效果吧 好的,开始废话了,实习差不多3个月了,这段时间跟着导师大佬也有接触过一些项目,也学到了不少东西,接触到的项目基本

IOS 改变导航栏返回按钮的标题实例详解

IOS 改变导航栏返回按钮的标题实例详解 前言: 下午又找到了一个新的方法 这个方法不错 暂时没有发现异常的地方. 新写的App中需要使用UINavigationController对各个页面进行导航,但由于第一级页面的title较长,在进入第二级页面后返回按钮leftButtonItem的title就会变得很长,对NavigationBar空间占用很大,而且不美观,于是使用代码对leftButtonItem的title文本进行修改,无论是设置self.navigationItem.leftBa

简单好用的iOS导航栏封装.runtime属性控制实例代码

前言 本文主要给大家介绍一个不错的导航栏控制工具,可以大大的简化代码,并保留系统特性,不用自定义导航栏,不用继承base. 下面话不多说了,来一起看看详细的介绍吧 UIViewController+YINNav 效果 @interface UIViewController (YINNav) /** 控制屏幕方向 在appdelegate 实现 - (UIInterfaceOrientationMask)application:(UIApplication *)application suppor

vue实现导航栏效果(选中状态刷新不消失)

Vue导航栏 用Vue写手机端的项目,经常会写底部导航栏,我这里总结一套比较方便实用的底部导航栏方法,并且可以解决浏览器刷新选中状态消失的问题.也可以选择自适应屏幕.看一下效果,底部的图标全是UI给的选中和未选中样式的图片,根据公司要求,你也可能会用fontsize去写.(全部代码黏贴到本文的最后面了) 1.首先把这些小图片放到src/assets路径下面(自动base64编码) 2.在data()里边定义一个选中对应的变量isSelect,和循环遍历的数组,数组下面放图标对应的文字,和选中,未

vue 中固定导航栏的实例代码

点击按钮回顶 <template lang="html"> <div class="gotop-box"> <i @click="gotop"class="icon topIcon"></i> </div> </template> <script> export default { methods: { gotop: function () {

前端Vue项目详解--初始化及导航栏

一.项目初始化 创建webpack模板项目如下所示: MacBook-Pro:PycharmProjects hqs$ vue init webpack luffy_project ? Project name luffy_project ? Project description A Vue.js project ? Author hqs ? Vue build standalone ? Install vue-router? Yes ? Use ESLint to lint your cod

vue实现二级导航栏效果

本文实例为大家分享了vue实现二级导航栏效果展示的具体代码,供大家参考,具体内容如下 实现如下功能: 在.vue文件中,template中的内容如下: <template> <div id="app"> <nav class="sidebar"> <ul class="menu"> <li v-for="(navList,index) in navLists" :key=&