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