<template>
<div id="svgTemplate"></div>
</div>
</template>
直接将svg文件的内容复制粘贴到.vue文件里,是可以在标签内直接添加@click事件完成需求的,方式简单但会造成文件过长,本文不多陈述
1.创建xhr对象
const xhr = new XMLHttpRequest();
this.svgUrl = ...;
xhr.open("GET", this.svgUrl, true);
xhr.send();
复制代码
2.监听xhr对象(获取svg的dom -> 添加事件 -> 修改dom -> 转成虚拟dom并挂载)
xhr.addEventListener("load", () => {
const resXML = xhr.responseXML;
this.svgDom = resXML.documentElement.cloneNode(true);
let btn = this.svgDom.getElementById("...");
btn.setAttribute("v-on:click", "this.handleClick()");
this.svgDom.getElementById("...").childNodes[0].nodeValue = ...
this.svgDom.getElementById("...").setAttribute("style",
`....; fill:${this.photoResult.resultColor}; ...`);
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("
// ↑↑↑ 原本的
复制代码
完整代码
<template>
<div id="svgTemplate"></div>
</div>
</template>
复制代码
<script>
import Vue from "vue/dist/vue.esm.js";
export default {
name: "svg-drawing",
data() {
return {
svgUrl: "",
svgDom: null,
photoResult: {
resultVal: 0,
resultMsg: "未检测",
resultColor: "#dcdee2"
async mounted() {
window["handleClick"] = () => {
this.takePhoto();
created() {
this.getSvg();
methods: {
getSvg() {
const xhr = new XMLHttpRequest();
this.svgUrl = this.baseUrl + "/svgs/" + "test.svg";
xhr.open("GET", this.svgUrl, true);
xhr.send();
xhr.addEventListener("load", () => {
const resXML = xhr.responseXML;
this.svgDom = resXML.documentElement.cloneNode(true);
let btnTakePhotoDom = this.svgDom.getElementById("...");
btnTakePhotoDom.setAttribute("v-on:click", "this.handleClick()");
this.svgDom.getElementById("...").childNodes[0].nodeValue = ...;
this.svgDom.getElementById("...").setAttribute("style",
`....; fill:${this.photoResult.resultColor}; ...`);
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>
复制代码