大致的流程是这样的:实现这个需求是首先通过一个工具(drawio)去自定义绘制图形,然后导出一个svg格式的文件,后端搞了一下drawio工具的源码,在导出的时候,为绘制的图形上每个节点都去绑定了一个id(具体是怎么搞得我一个前端也不是太清楚),最后导出,然后前端需要去加载svg,然后每个节点都会有对应的数据(就是根据绑定的id去获取),然后有些节点上还可能有图片,还需要替换,还可能为每个节点去绑定事件做一些事情。然后还使用了d3实现了让svg可以缩放拖拽。大概是这个亚子
先上一张自己测试用的svg
 如果使用 img标签加载出来的就是上图的效果,仅仅是一张静态图片,这样是无法实现咱们的需求的。
 如果使用 img标签加载出来的就是上图的效果,仅仅是一张静态图片,这样是无法实现咱们的需求的。
 当时看到类似这样的需求的时候,一脸懵,之前也没做过类似的功能,不知道从何下手。。
 没办法只能去找一些相关的资源、案例、百度等等,找了好多,最终还是搞出来了。
首先说下思路:
1. 首先咱们要通过XMLHttpRequest的方式去加载svg,因为这样才能去操作svg中的dom元素
2. 接下来就是拿到里面的dom元素集合,遍历为某些节点(当然也可以为整个svg)绑定事件,注意:这里绑定的事件是在window层上,我们要向window上挂载绑定的方法
3. 然后就可以把svg挂载到容器dom上,这里注意:不能通过DOM.appendChild的方式去追加,要先转成虚拟dom并挂载,否则绑定的事件失效
4. 挂载dom上之后就能看到正常的我们加上绑定事件的svg了,这时候也在dom上了,咱们可以去获取里面的dom,循环遍历去获取每个节点的id,并找到里面对应的font标签并将其数据进行修改,除了font标签外、如果有图片还会有image标签
然后下面是完整的代码,代码注释还是蛮详细的,希望可以帮到大家
<template>
  <div>
    <div id="svgTemplate" ref="svg"></div>
  </div>
</template>
<script>
import wftSvg from "@/assets/img/wft.svg";
import Vue from 'vue/dist/vue.esm.js'
import * as d3 from "d3"; //在vue文件里面引入d3
export default {
  data() {
    return {
      svgDom: null,
      allDom: null
    };
  },
  created() {
    this.getSvg();
  },
  mounted() {
    //  svg 点击事件
    window['handleClick'] = function(e,currNodeId) {
      let tag = e.srcElement || e.target;
      console.log(e, '点击----->>>')
    },
    //  svg 鼠标滚动事件
    window['havcZooming'] = (e) => {
      console.log(e, 'havcZooming----->>>')
      this.zoomimg();
    }
  },
  methods: {
    getSvg() {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", wftSvg, true);
      xhr.send();
      xhr.addEventListener("load", () => {
        const resXML = xhr.responseXML  //  this.stringToXml(xhr.responseXML)
        this.svgDom = resXML.documentElement.cloneNode(true); // 克隆节点
        // console.log(this.svgDom, '----->>>')
        // 为 svg - dom 设置宽高边框样式
        this.svgDom.style.width = "100%";
        this.svgDom.style.height = "80vh";
        this.svgDom.style.border = "1px solid yellow";
        // 为svg添加鼠标滚动缩放事件
        this.svgDom.setAttribute("v-on:mousewheel", "this.havcZooming($event)");
        // svg - a
        let adomNodeAll = this.svgDom.getElementsByTagName("a");
        //  循环修改节点样式 添加事件
        for(let i = 0; i < adomNodeAll.length; i++) {
          adomNodeAll[i].style.cursor = 'pointer' // 修改节点样式
          let currNodeId = adomNodeAll[i].getAttribute('id')
          adomNodeAll[i].setAttribute("v-on:click", "this.handleClick($event, '"+ currNodeId +"')"); // 为每个节点绑定点击事件
          let currNode = this.svgDom.getElementById(currNodeId)
        }
        //  设置 id 属性
        let gtag = this.svgDom.getElementsByTagName("g");
        gtag[0].setAttribute("id", "svgcanvas");
        /* 将svgDom对象转换成vue的虚拟dom */
        var oSerializer = new XMLSerializer();
        var sXML = oSerializer.serializeToString(this.svgDom);
        var Profile = Vue.extend({
            template: "<div>" + sXML + '</div>'
        });
        // 创建实例,并挂载到元素上
        new Profile().$mount('#svgTemplate');
        // document.getElementById('svgTemplate').appendChild(this.svgDom)
        //  svg - g
        let svgcanvasDom = document.getElementById("svgcanvas");
        this.allDom = svgcanvasDom.getElementsByTagName("a");
        for(let i = 0; i < this.allDom.length; i++) {
          // console.log(this.allDom[i].childNodes,'childNodes');
          let curraNodeId = this.allDom[i].id // 当前a标签节点的id,我们可以根据id获取对应节点数据并将其渲染对应的font标签上
          // console.log(curraNodeId, '当前a标签下的节点id----->>>')
          if(this.allDom[i].childNodes.length) { // 如果a标签下还有子元素
            //  修改节点数据(a 标签里面有 font 标签 即节点的数据)
            let fontDom = this.allDom[i].getElementsByTagName("font");
            if(fontDom.length) {
              // 可以根据 curraNodeId 获取该节点对应的数据并渲染到font标签上
              fontDom[0].innerHTML = 'self'
            }
            //  修改图片(a 标签中有 image 标签 , 即节点图片)
            let imageDom = this.allDom[i].getElementsByTagName("image");
            if(imageDom.length) {
              imageDom[0].attributes['xlink:href'].value = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAADUElEQVQ4jY2VzW8bRRiHn9kZ7/o7thPbbVGT8NUIQSDtja+ey4EiJFSJSnBBoP4LqBd6QhyQuFW5cUKCAwgu7V/AASkUVDWQFiq3gbYhdrCdxPs5M2gXg8ohZN/L7L6jffTO+/72N8JaS564dPEiFW/DdrtdavU6SZygtUUIwWg85NbNdWK7IlQuGtDvDxjbHicebfH8i08z2hmxs9UnSiyjwRa/3d1EVY6TGyiVhw0trhLMtZtIY/B3HmCMRSkH1y3ilhs4eYGN5jKIEmEUE4UxxgoMkrRlQRggC3VmWyv5gZX6C0wmitFoTBgESOkgpYu2DqPRgCSpUy6fRImvX89HXIJ31gRxWp3WIARCOBgjiKMYbRwQ5f9UuABcBu4A4XS9PM1nYW2EcJzsmFiNMSk8xqJIkj5R9MO/wDPAdeACMA+40/XCNH/m7KcPbLniY4WTkkFYtA6JIx/pCOYaksnwS5tOeRH4HKgdcNg0/8UTKy3mmVB0BUkcopSHEG7WyyOdOnNNhxs/Xctk8z5QP6SDtdunupyvHENIRWwsjhXUGm1UsUqr3UFYn+0/dzPgK3lmsuZannvpXSwGaywCSa1ts36qgkK6mrX1rQzYzQPcivZQ7hHAADod0UO7MstZqzLgFnD8MOAskp+/W8VY8zdCuVidQtJnhUkCere+z4BXgPcOAz65s8+3N69grINyXBCSKA4QQKlUJYp8fr93PwN+CLz5P1NOY/eZ69u1XuKQaloVPDy3iDapFjVuweD7Y4JQZTrsAefSjw6CpfvD3szoxsaQPwYBnaNHWXrqBJ12m0QX+KW3zY/rmwi1cOkfYV8FloFVYBOIpuvqNH/1s7fKDWPKPL44y9lXT3P65WVOLc9z8tlFqhWHMCrSfeTcBw/b153pn3FgtJsl5lplZuoVUq8pepLZRoV2q8R2E2q1x1D2ta8Om0cWH3/0DRveJ5lLR1FMQTqZF6bvUMAk9wj8a/kNtqJuQ6GAUi46FbbVGSzFeZ6H60Iw+TW/H+6Ot3GjPjMztQzq+wGJtpkGq9UyjmPZ2x/nrzC7lKymWCpQb1TRsZ8WjCckUgqSJEJYm7/CzrE3CPdb7N0fM7w7IByE6DHEg4igP8STS8wvvM1fZW5twKdzQs0AAAAASUVORK5CYII='
            }
            //  设置支路的移动动画 (有 getTotalLength 即为线节点)
            if(this.allDom[i].childNodes[0].getTotalLength) {
              // 使不同长度线路动画速度一致
              let length = this.allDom[i].childNodes[0].getTotalLength()
              let duration = length / 50
              let animationString = duration.toFixed(0) + "s " + "linear infinite hacvRun";
              // console.log(length, "length");
              this.allDom[i].childNodes[0].style.strokeDashoffset = length;
              this.allDom[i].childNodes[0].style.animation = animationString;
            }
          }
        }
      });
    },
    zoomimg(x, y) {
      // 放大缩小
      // 缩放事件绑定给svg,缩放结果设置给svg内部的g标签
      if (!x) {
        x = 0;
      }
      if (!y) {
        y = 0;
      }
      const svg = d3.select("svg");
      const g = d3.select("#svgcanvas");
      // console.log(svg, g, "in havcZooming");
      //节点的缩放
      function zoomActions() {
        // console.log(d3.event, '----->>>') // undefind
        // g.attr("transform", d3.event.transform);
        g.attr("transform", d3.zoomTransform(svg.node()));
      }
      let zoomHandler = d3.zoom().on("zoom", zoomActions).scaleExtent([0.5, 40]);
      // zoomHandler(svg)
      svg.call(zoomHandler);
      svg.transition().duration(750).call(zoomHandler.transform, d3.zoomIdentity.translate(-x, -y).scale(2));
      // // 点击按钮定位
      // d3.select("#reset").on("click", function () {
      //   svg
      //     .transition()
      //     .duration(750)
      //     .call(zoomHandler.transform, d3.zoomIdentity);
      // });
      // d3.select('#pos1').on('click',function(){
      //   svg.transition().duration(750).call(zoomHandler.transform, d3.zoomIdentity.translate(-10,-1500).scale(2));
      // });
      // d3.select('#pos2').on('click',function(){
      //   svg.transition().duration(750).call(zoomHandler.transform, d3.zoomIdentity.translate(-1200,-10).scale(2));
      // });
    },
    stringToXml(xmlString) {
      let xmlDoc = null
      if (typeof xmlString == "string") {
        //FF
        if (document.implementation.createDocument) {
          var parser = new DOMParser();
          xmlDoc = parser.parseFromString(xmlString, "text/xml");
        } else if (window.ActiveXObject) {
          xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
          xmlDoc.async = false;
          xmlDoc.loadXML(xmlString);
        }
      }else {
        xmlDoc = xmlString;
      }
      return xmlDoc;
    }
  }
};
</script>
<style>
@keyframes dash {
  to {
    stroke-dashoffset: 0;
  }
}
@keyframes run {
  from {
    stroke-dasharray: 10, 5;
  }
  to {
    stroke-dasharray: 40, 5;
  }
}
@keyframes hacvRun {
  from {
    stroke-width: 6;
    /* stroke-dashoffset: 0; */
    stroke-dasharray: 10, 8;
  }
  to {
    stroke-width: 6;
    stroke-dashoffset: 0;
    stroke-dasharray: 10, 8;
  }
}
</style>最后是搞完之后的效果:
 
  
补充:
上面代码通过
var Profile = Vue.extend({
  template: "<div>" + sXML + '</div>'
});
// 创建实例,并挂载到元素上
new Profile().$mount('#svgTemplate');这个方式去挂载,如果使用错误警告或者有问题可以看下我上篇博文:
【解决】You are using the runtime-only build of Vue where the template compiler is not available
上面使用了d3,记得npm 装一下呢
npm install d3                










