Vue 中 SVG 动态渲染 包括(绑定事件,修改元素,SVG缩放) 支持PC 移动端

Vue 中 SVG 动态渲染 包括(绑定事件,修改元素,SVG缩放) 支持PC 移动

业务需求: SVG动态渲染页面 可以修改SVG元素属性,点击事件,SVG的缩放移动

vue + svg + D3 借鉴了一些其他博主的案例来实现

template

页面默认设置 存入 svg 模块

<template>
  <div>
    <div class="back"></div>
    <div id="svgTemplate"></div>
    <button style="z-index: 10;position: absolute;" @click="Promotion(6124)">促销</button>
  </div>
</template>

script

引入 默认的Vue引入可能会出现报错 使用以下方式,
引入 D3

import Vue from "vue/dist/vue.esm.js";
import * as d3 from "d3"; 

动态数据
首先
1.请求的方式拿到SVG的数据流
2.拿到SVG的dom节点
3.给SVG的dom节点设置属性,设置事件 并绑定到window全局上
4.在SVG的dom节点上做逻辑处理(我这里是:一些条件做修改rect标签的样式 属性等 )
5.然后将svg转成虚拟DOM , 虚拟DOM 挂载到一开始就创建的模块


6.然后使用缩放 用的D3 处理 ,并在PC 和 移动端上实现 (移动端目前只添加了手指事件 无其他处理)
7.注:页面的事件处理 修改SVG dom样式可以修改 (添加SVG下rect标签 可添加但是视图未更新)使用的添加节点重新渲染 (更好的方法可以打在评论)

在这里插入图片描述

async mounted() {
    // 方法绑定到window下面,提供给外部调用
    // svg rect点击事件
    window["handleClick"] = (e, objectAll) => {
      let tag = e.srcElement || e.target;
      let data = JSON.parse(objectAll)
      this.takePhoto(tag, data);
    };
    // svg 缩放 移动事件
    window['havcZooming'] = (e) => {
      this.zoomimg(e);
    }
  },
 data() {
    return {
      /* 全局 */
      svgUrl: "", // svg的url
      svgDom: null, // 获取到的svg元素
      svgimg: require('../assets/2.svg'),
      // stroke:'',
      stroke: [6124, 6123],
      linkShow: '2'
    };
  },

created() {
    this.getSvg();
  },
// 初始化SVG  
 methods: {
    // 初始化svg
    getSvg() {
      /* 创建xhr对象 */
      const xhr = new XMLHttpRequest();
      this.svgUrl = this.svgimg;
      xhr.open("GET", this.svgUrl, true);
      xhr.send();
      /* 监听xhr对象 */
      xhr.addEventListener("load", () => {
        /* 1. 获取 dom */
        const resXML = xhr.responseXML;
        this.svgDom = resXML.documentElement.cloneNode(true);
        //  给svg 设置 id 属性
        this.svgDom.setAttribute("id", "svgcanvas");
        //  给svg添加鼠标 滚动 缩放 事件   // PC 鼠标滚动  移动端 手指事件
        this.svgDom.setAttribute("v-on:mousewheel", "this.havcZooming($event)");
        this.svgDom.setAttribute("v-on:ontouchstart", "this.havcZooming($event)");
        this.svgDom.style.zIndex = '9'
        // /* 2.SVG对象添加click事件 */
        let btnTakePhotoDom = this.svgDom.querySelectorAll("rect");
        // /* 3.SVG对象 逻辑处理 */
        //;逻辑处理 starts -----
        if (this.linkShow == '1') {
          // 创建新的 、rect 标签 添加到虚拟dom 重新渲染
          this.createRect()
        }
        //如果促销车位是一个数组 
        let strokes = [6124, 6123]
        for (let i of btnTakePhotoDom) {
          //判断某ID 是否存在或多个ID 改变背景颜色和 边框颜色
          console.log(i['id'], 'id')
          //去掉循环
          for (let arrkey of strokes) {
            //i['id'] == this.stroke
            if (i['id'] == arrkey) {
              i.setAttribute('stroke', 'red')
            }
          }
          let objectAll = {
            currNodeId: i.getAttribute('id'),
            currNodeX: i.getAttribute('x'),
            currNodeY: i.getAttribute('y'),
            currNodeWidth: i.getAttribute('width'),
            currNodeHeight: i.getAttribute('height'),
          }
          i.setAttribute("v-on:click", "this.handleClick($event,'" + JSON.stringify(objectAll) + "')");
        }
        // end----



        // /* 4.将svgDom对象转换成vue的虚拟dom */
        var oSerializer = new XMLSerializer();
        var sXML = oSerializer.serializeToString(this.svgDom);
        var Profile = Vue.extend({
          template: "<div id='svgTemplate' ref='svgAll' style='z-index: 2;position: fixed;left: 0;top: 0;width:100%;height:100%;'>" + sXML + "</div>"
        });
        // 创建实例,并挂载到元素上
        new Profile().$mount("#svgTemplate");
      });
    },
    // 事件
    takePhoto(e, data) {
      // let arr = [6123,6124,6122]
      let arr = document.querySelectorAll('rect')

      for (let i of arr) {
        if (i['id'] == data.currNodeId) {
          i.setAttribute('fill', 'green')
        } else {
          i.setAttribute('fill', '#fff')
        }
      }
      console.log(data)
      // let arr = document.querySelectorAll('rect')
      // let dom = document.querySelector('svg')
      // console.log(dom)
      // let divSFYX=window.document.createElement("rect")
      // divSFYX.setAttribute('id','6125')
      // divSFYX.setAttribute('x','1419.2')
      // divSFYX.setAttribute('y','364.64')
      // divSFYX.setAttribute('width','294.4')
      // divSFYX.setAttribute('height','755.2')
      // divSFYX.setAttribute('fill','pink')
      // divSFYX.setAttribute('stroke','red')

      // console.log(divSFYX.nodeType);

      // dom.appendChild(divSFYX)

    },
    //svg 缩放
    zoomimg(e) {
      var x = e.x
      var y = e.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() {
        // g.attr("transform", d3.event.transform);
        g.attr("transform", d3.zoomTransform(svg.node()));
      }
      let zoomHandler = d3.zoom().on("zoom", zoomActions).scaleExtent([1, 10]);

      // 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('#svgcanvas').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));
      // });
    },
    // 页面按钮
    Promotion(id) {
      console.log(id)
      this.linkShow = '2'
      this.getSvg()
      // this.created()
      // let arr = document.querySelectorAll('rect')
      // for(let i of arr){
      //   if(i['id'] == id){
      //     i.setAttribute('fill', 'green')
      //   }else{
      //     i.setAttribute('fill', '#fff')
      //   }
      // }
      // this.stroke = id
      // this.getSvg();

    },
    createRect() {
      const coordinate = [
        {
          id: '6125',
          x: '10',
          y: '10',
          width: '100',
          height: '1',
          fill: 'pink',
          stroke: 'red'
        },
        {
          id: '6126',
          x: '110',
          y: '10',
          width: '1',
          height: '100',
          fill: 'pink',
          stroke: 'red'
        },

      ]
      for (let i of coordinate) {
        let divSFYX = document.createElement("rect")
        divSFYX.setAttribute('id', i.id)
        divSFYX.setAttribute('x', i.x)
        divSFYX.setAttribute('y', i.y)
        divSFYX.setAttribute('width', i.width)
        divSFYX.setAttribute('height', i.height)
        divSFYX.setAttribute('fill', i.fill)
        divSFYX.setAttribute('stroke', i.stroke)
        divSFYX.style.transform = 'rotate(405deg)'
        this.svgDom.appendChild(divSFYX)
      }

    }
  },


beforeDestroy() {
    this.svgDom = null;
  },


<style scoped>
.back {
  width: 100vw;
  height: 100vh;
  position: fixed;
  left: 0;
  top: 0;
  z-index: 1;
  background: rgba(0, 0, 0, 0.2);
}

#svgTemplate {
  z-index: 2;
  position: fixed;
  left: 0;
  top: 0;
}
</style>

页面效果

在这里插入图片描述

因为我的比较简单,前期用少量的数据来实现页面处理 操作,后期换较大的SVG实现

补充优化一下
上面写的D3的鼠标拖拽 缩放有卡顿,替换成其他的(实例)

	let svg = d3.select("#svgcanvas"); //svgcanvas这个是svg这个标签的id
      var zoom = d3.zoom().on("zoom", function () { // svg放大缩小的事件
        d3.select(this).selectAll("g").attr("transform", d3.zoomTransform(svg.node()));
      });
      svg.call(zoom);

修改了页面的SVG 效果如下,也只是一部分 (移动端),后期在替换更大的SVG,在补充
在这里插入图片描述

分割线----------------------------.

接上续 功能还是有些小瑕疵 最终是要在手机端 实现的 整个SVG 的 缩放和拖拽都不可用D3的 (该死啊!!!!)

也找了很多资料H5的事件 最终最终还是给解决了 (渐进脑汁啊~)
这篇文章 有补充
在这在这 点我点我 XDM

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值