关于svg图纸实现点击坐标修改svg参数
最近公司有一个需求,挺有意思的实现方式,花了点功夫实现了
需求是: 给定一张图纸,如果点击特定的坐标矩阵内,则弹出弹窗进行修改
一开始是想着用png来实现,但是图纸又有放大缩小拖拽的需求,加上后端返回的cad源文件的坐标不准(cad图纸的定位是根据cad文件的原点进行定位而不是图纸左上角定位),翻阅svg的特点找到以下实现方式
实现思路:
1.先获取svg的文件流,设置svg容器铺满屏幕
<div class="el" v-html="svgString"></div>
getSvgByFileId(this.fileId).then(res => {
this.svgString = res
})
2.svg实现拖拽缩放功能
this.$nextTick(() => {
const el = document.querySelector('.el')
// svg 缩放
el.onwheel = (e) => {
const viewBox = svg.getAttribute("viewBox");
const [x, y, width, height] = viewBox.split(/\s+/).map(parseFloat);
const scaleDelta = e.deltaY < 0 ? 0.9 : 1.1;
const newWidth = width * scaleDelta;
const newHeight = height * scaleDelta;
const dx = (width - newWidth) / 2;
const dy = (height - newHeight) / 2;
const newViewBox = `${x + dx} ${y + dy} ${newWidth} ${newHeight}`;
svg.setAttribute("viewBox", newViewBox);
};
// svg拖拽移动
let clientX = 0; // 鼠标的最后一个x轴位置
let clientY = 0; // 鼠标的最后一个Y轴位置
let debounce = true; // 是否节流
let isStartMoveSvg = false; // 是否开始拖动
let ratio = 1; // 拖动的速度
let viewBox; // viewBox
let arrPoint; // 先声明svg的viewBox属性
// 点击后开始拖拽
el.onmousedown = () => {
isStartMoveSvg = true;
const width = el.getBoundingClientRect().width;
viewBox = svg.getAttribute("viewBox");
arrPoint = viewBox.split(/\s+/).map(parseFloat);
ratio = arrPoint[2] / width;
if (ratio > 1) ratio = 1;
};
// Mouse up means end moving
el.onmouseup = () => {
isStartMoveSvg = false;
clientX = 0;
clientY = 0;
};
// Dynamically set "viewBox" while moving
el.onmousemove = (e) => {
if (debounce) {
debounce = false;
if (isStartMoveSvg) {
if (clientX !== 0 && clientY !== 0) {
arrPoint[0] = arrPoint[0] - (e.clientX - clientX) * ratio;
arrPoint[1] = arrPoint[1] - (e.clientY - clientY) * ratio;
svg.setAttribute("viewBox", arrPoint.join(" "));
}
clientX = e.clientX;
clientY = e.clientY;
}
setTimeout(() => {
debounce = true;
}, 50);
}
};
})
})
3. 给svg绑定双击事件
svg的坐标系和浏览器的坐标系有差异,所以比较的时候需要用translateToSVG方法先将点击事件的坐标转成svg的坐标,在svg中每一个矩阵都可以是一个path,获取所有的 path,然后每次点击的时候循环遍历,数据量过大可以根据返回的svg去过滤path,总结出想要的path矩阵
const svg = document.querySelector('.el > svg')
const _this = this
svg.addEventListener('dblclick', (e) => {
console.log(e)
let zoom = svg.style.zoom ? (Number(svg.style.zoom.replace('%', '') / 100)) : 1
let x = e.offsetX / zoom
let y = e.offsetY / zoom
filterPaths.forEach(path => {
_this.comparePosition(svg, path, x, y)
})
})'
methods:{
comparePosition(svg, path, x, y) {
const pointBox = path.getBBox()
const [pointX, pointY] = translateToSVG(svg, [x, y])
// console.log(pointX, pointY)
if (pointX > pointBox.x &&
pointX < pointBox.x + pointBox.width &&
pointY > pointBox.y &&
pointY < pointBox.y + pointBox.height
) {
// 坐标在矩形中
console.log(path)
let data = {
hexCode: path.previousElementSibling.children[1].textContent,
text: path.previousElementSibling.children[4].textContent,
}
this.editCode(data)
}
},
}
translateToSVG方法
export function translateToSVG(svg, points) {
const output = [];
const transformationMatris = svg.getScreenCTM()?.inverse();
for (let i = 0; i < points.length; i += 2) {
const point = DOMPoint.fromPoint({ x: points[i], y: points[i + 1] });
const transformationPoint = point.matrixTransform(transformationMatris);
output.push(transformationPoint.x, transformationPoint.y);
}
return output;
}