vue dynamic rendering svg, add a click event

Welcome attention to [the front end of the small Ou GitHub] , read more original technical articles

Business needs (vue project)

1. display svg content page
2. Click event monitor internal svg
3. dynamically change properties and values svg interior elements

html tags

After several experiments, embed, img src attribute label changes and other ways, are unable to achieve the full functionality (especially svg internal click event), the eventual adoption ** Vue.extend () ** method complete implementation of the code is also more simple, html structure is as follows:

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

Simply copy and paste the contents of the file .vue svg file that can be added directly to the needs of @click event is completed within the label, simple way but will cause the file is too long, this little statement

Realization of ideas

1. Create an object xhr

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

2. monitor xhr objects (acquisition of svg dom -> add an event -> Modify dom -> turn into virtual dom and mount)

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. The event methods to be performed in the following bind to the window, for external (handleClick event just added) call

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

Here basically completed requirements: dynamic rendering svg, svg modify the attributes and values of the components of a grammar js operating dom, to add a dynamic component svg event handleClick, finally bind takePhoto () event to the handleClick window object, you can rest assured that bold in takePhoto () to write what you want to perform up!

Special Note

  • Svg dom to parts when adding events:
    1. After several attempts, only setAttribute + v-on: click wording effective
    2.setAttribute does not support @click (non-native events), it will report a syntax error
    3.addEventListener and will onclick He was intercepted vue

  • When you convert svgDom objects into the virtual dom vue:
    1. If the following error
    Here Insert Picture Description
    will import Vue from "vue"change import Vue from "vue/dist/vue.esm.js"
    , its causes and solutions This article does not discuss other self-Baidu.

    2.vue.extend () method is a constructor of vue, for instance dynamically created vue, Template template assembly can have only one root element

    3. $ mount manually mount to the id of the element svgTemplate, replacing the original dom will mount (replace the original <div id="svgTemplate"></div>). Because each update svg have to remount, did not find a dom element it is unable to mount, so the template inside the outermost div id have to add attributes:

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

The complete code

<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>
Published 17 original articles · won praise 15 · views 20000 +

Guess you like

Origin blog.csdn.net/Simon9124/article/details/104817429