
VUE2 - 移动端(h5)的SVG 缩放拖拽( 一 )
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue

·
fine 很好 苦心专研 弄出来了
VUE2 - 移动端(h5)的SVG 缩放拖拽
一开始想着用一些插件 组件实现 ,但是我是虚拟DOM挂载的 (可能技术有限 没搞懂),自己手写了一个,嘿嘿
下面是代码 包括上篇文章的 PC的SVG拖拽缩放在一块。
思路很简单 一开始给想复杂了
1.还是xml的形式去获取SVG文件dom
2.拿到DOM解析后 在SVG上进行 事件绑定 就是(手指事件)触摸,滑动,松开
3.去控制SVG的位置 (我用的定位left 和 top 更好是tranform 比较好,后续我在优化,先实现功能)
4.按下的时候去分别记录 单指 双指的操作(存元素位置,和手指按下的位置 下面代码有详细注释)
5.反正 emm 直接看代码!!!
<template>
<div>
<div id="svgTemplate"></div>
</div>
</template>
<script>
import Vue from "vue/dist/vue.esm.js";
import * as d3 from "d3"; //在vue文件里面引入d3
export default {
name: "svg-2D",
data() {
return {
/* 全局 */
svgUrl: "", // svg的url
svgDom: null, // 获取到的svg元素
/* svg的变量 */
photoResult: {
resultVal: 0, // 测试结果 - 值
resultMsg: "未检测", // 测试结果 - 字段
resultColor: "#dcdee2" // 测试结果 - 字段背景色
},
svgimg: require('../../assets/test10.svg'),
// stroke:'',
// stroke: [6124, 6123],
// linkShow: '1',
oldMousePos: {},
oldMousePos2: {},
isTouch: false,
num: 1
};
},
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);
};
window['touchstart'] = (e) => {
this.touchstart(e);
}
window['touchMove'] = (e) => {
this.touchMove(e);
}
window['touchEnd'] = (e) => {
this.touchEnd(e);
}
document.addEventListener('gesturestart', function (event) {
event.preventDefault()
})
},
created() {
this.getSvg();
},
methods: {
// handleZoom(event) {
// const scale = event.transform.k; // 获取缩放比例
// const translate = [event.transform.x, event.transform.y]; // 获取平移距离
// // 计算SVG的中心点
// const centerX = this.svgWidth / 2;
// const centerY = this.svgHeight / 2;
// // 根据缩放和平移距离计算中心点的位置
// const center = [
// centerX / scale - translate[0] / scale,
// centerY / scale - translate[1] / scale,
// ];
// // 将中心点设置为可视区域
// this.d3Instance.translateTo(event.target, center[0], center[1]);
// },
// 初始化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:touchstart", "this.havcZooming($event)");
this.svgDom.setAttribute("v-on:touchstart", "this.touchstart($event)");
this.svgDom.setAttribute("v-on:touchmove", "this.touchMove($event)");
this.svgDom.setAttribute("v-on:touchend", "this.touchEnd($event)");
this.svgDom.style.width = '97vw'
this.svgDom.style.height = '97vh'
this.svgDom.style.zIndex = '9'
this.svgDom.style.position = 'absolute';
// /* 2.SVG对象添加click事件 */
let btnTakePhotoDom = this.svgDom.querySelectorAll("rect");
let btnTakePhotoDomPath = this.svgDom.querySelectorAll("path");
// 设置SVG》img的初始坐标系
let svgImage = this.svgDom.querySelector('image')
// svgImage.setAttribute('z-index', '1')
// /* 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('z-index', '9')
i.setAttribute('fill', 'pink')
i.setAttribute("v-on:click", "this.handleClick($event,'" + JSON.stringify(objectAll) + "')");
}
for (let i of btnTakePhotoDomPath) {
let objectAll = {
currNodeId: i.getAttribute('id'),
}
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) {
console.log(data)
this.$emit('searchShowTrue', data)
},
//svg 缩放
zoomimg(e) {
console.log('111111545')
// ----------------
let svg = d3.select("#svgcanvas"); //svgcanvas这个是svg这个标签的id
var zoom = d3.zoom().on("zoom", function () { // svg放大缩小的事件
d3.select(this).selectAll("rect").attr("transform", d3.zoomTransform(svg.node()));
d3.select(this).selectAll("path").attr("transform", d3.zoomTransform(svg.node()));
d3.select(this).selectAll("image").attr("transform", d3.zoomTransform(svg.node()));
});
svg.call(zoom);
},
// 页面按钮
Promotion(id) {
console.log(id)
this.linkShow = '2'
this.getSvg()
},
createRect() {
const coordinate = [
{
id: 'B1F_CW_8125_2',
x: '10835',
y: '14129',
width: '137',
height: '56',
fill: '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.style.transform = 'rotate(405deg)'
this.svgDom.appendChild(divSFYX)
}
// let divSFYX = document.createElement("rect")
// divSFYX.setAttribute('id', '6448')
// divSFYX.setAttribute('x', '6008')
// divSFYX.setAttribute('y', '6458')
// divSFYX.setAttribute('width', '50')
// divSFYX.setAttribute('height', '50')
// divSFYX.setAttribute('fill', 'red')
// divSFYX.setAttribute('stroke', 'red')
// this.svgDom.appendChild(divSFYX)
},
touchstart(e) {
console.log('按下', e.touches.length)
if (e.touches.length > 0 && e.touches.length < 2) {
//单指按下
this.isTouch = true;
const selectDom = e.currentTarget;
const { pageX, pageY } = e.touches[0]; // 手指位置
const offsetLeft = document.getElementById('svgcanvas').style.left
const offsetTop = document.getElementById('svgcanvas').style.top
// 手指原始位置
this.oldMousePos = {
x: pageX,
y: pageY,
};
// 元素原始位置
this.oldNodePos = {
x: offsetLeft == '' ? 0 : parseInt(offsetLeft.replace('px', '')),
y: offsetTop == '' ? 0 : parseInt(offsetTop.replace('px', '')),
};
// console.log(pageX, pageY, this.oldNodePos, '第一次点击的位置')
} else {
//双指按下
const selectDom = e.currentTarget;
const { pageX, pageY } = e.touches[0]; // 手指位置
const { pageX2, pageY2 } = e.touches[1];//2
// 手指原始位置
this.oldMousePos = {
x: pageX,
y: pageY,
};
this.oldMousePos2 = {
x: pageX2,
y: pageY2,
}
let point1 = e.touches[0];
let point2 = e.touches[1];
let xLen = Math.abs(point2.pageX - point1.pageX);
let yLen = Math.abs(point2.pageY - point1.pageY);
let sqrtS = Math.sqrt(xLen * xLen + yLen * yLen)
this.sqrt = sqrtS
console.log(sqrtS, '点击的时候两个点的距离')
// 元素原始位置
}
},
touchMove(e) {
console.log('拖到')
//----
const selectDom = e.currentTarget;
// e.preventDefault(); // 取消事件的默认动作。
// 移动操作
if (e.touches.length === 1 && this.isTouch) {
console.log('单指')
const selectDom = e.currentTarget;
const { pageX, pageY } = e.touches[0]; // 手指位置
selectDom.style.left = `${pageX - this.oldMousePos.x + this.oldNodePos.x}px`
selectDom.style.top = `${pageY - this.oldMousePos.y + this.oldNodePos.y}px`
// console.log(pageX - this.oldMousePos.x, this.oldNodePos.x, '移动后的位置')
} else {
let point1 = e.touches[0];
let point2 = e.touches[1];
let xLen = Math.abs(point2.pageX - point1.pageX);
let yLen = Math.abs(point2.pageY - point1.pageY);
console.log(xLen, yLen, 'lianggdian')
let sqrtS = Math.sqrt(xLen * xLen + yLen * yLen)
console.log(sqrtS, '两个点的距离')
this.num <= 1 ? this.num = 1 : this.num
if (this.sqrt < sqrtS) {
this.num += 0.03
selectDom.style.transform = "scale(" + this.num + ")";
} else {
this.num -= 0.03
selectDom.style.transform = "scale(" + this.num + ")";
}
}
},
touchEnd(ev) {
console.log('松开')
if (this.isTouch) {
this.isTouch = false;
}
},
},
beforeDestroy() {
this.svgDom = null;
},
watch: {
photoResult: {
handler(newVal, oldVal) {
this.getSvg();
},
deep: true
}
}
};
</script>
<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;
}
.city {
position: relative;
min-width: 100vw;
min-height: 100vh;
color: white;
}
.home {}
</style>
实现了这个东西第一时间就发出来了,很苦恼这个东西
希望可以帮助 有这方面需求的小伙伴去解决
加油,今天周六我在 加班中~~~ 哦莫
哦对了 附在一个视频
移动端
还有个问题 H5在微信游览器 我缩放偶尔会 缩放网页 !!!
2023-3-8. 13:17
很好 解决了
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0,user-scalable=no"/>
meta标签加上这个
根页面加上 去监听限时 网页缩放 APP.vue
window.onload = function() {
document.addEventListener('touchstart', function(e) {
console.log("1手指",)
if (e.touches.length > 1) {
e.preventDefault()
}
})
document.addEventListener('gesturestart', function(e) {
console.log("2指")
e.preventDefault()
})
}
此文章是手动实现的缩放效果不太友好,后面使用了一些库实现了,效果极佳




vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:5 个月前 )
9e887079
[skip ci] 3 个月前
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 7 个月前
更多推荐
所有评论(0)