前提
此方法限制太多,可能
svg使用d3绘制,并且抽象出svg中所有元素的一个参照点和缩放比例
svg元素不会太多,否则会造成卡顿。
引入
最近有个项目需要我帮一下前端,主要是使用d3绘制svg放在页面,其中有一个功能就是对绘制的svg进行拖动和缩放,有点像地图。
这里我已经写好了一个方法来绘制svg
function drawTopo(svg, data, x, y, svgA, radio)
其中svg是要绘制的元素,data是要绘制的数据,x,y是svg的中心坐标,svgA是父元素的边长,来控制svg(正方形)刚好在父元素内,radio是缩放比例
思路与实现
实现方式是重绘,所以元素太多会造成卡顿。
一、拖拽
我们来分析拖拽的过程,鼠标按下---->鼠标移动------>松开鼠标。对应的事件分别是mousedown、mousemove、mouseup,先定义两个全局变量
data() {
return {
dragging:false,
mousePos:{x:0, y:0},
}
}
鼠标按下事件
mousedown(e) {
const event = window.event || e
this.dragging = true;
this.mousePos.x = event .clientX;
this.mousePos.y = event .clientY;
},
//鼠标按下,准备拖拽,记录当前鼠标位置
鼠标移动事件
mousemove(e) {
const event = window.event || e
if(!this.dragging) {
return ;
}
let nx = event .clientX;
let ny = event .clientY;
//计算偏移坐标
let offsetX = nx - this.mousePos.x;
let offsetY = ny - this.mousePos.y;
//drag方法会把svg删除再重绘
this.drag(offsetX, offsetY);
//继续记录鼠标位置
this.mousePos.x = nx;
this.mousePos.y = ny;
},
最后放开鼠标
mouseup(e) {
this.dragging = false;
},
一个问题,毋庸置疑这三个事件都注册在svg元素(或者与svg等大的父元素)上,但是当鼠标拖到svg外面时,在svg外面放开了鼠标,鼠标回到svg中,图形会随着鼠标移动,这样是不应该的,所以应该把最后一个事件mouseup注册到最外面的元素上,那么鼠标在svg外放开也可以触发。
二、缩放
缩放的实现和拖拽类似,相同的就不赘述了。
缩放是根据鼠标滚轮的事件触发,但是鼠标滚轮有他的默认事件,那就是页面滚动,这里要阻止它。
mousewheel(e) {
const event = window.event || e
e.preventDefault();
let newRadio = this.radio;
if(e.deltaY > 0) {
newRadio -= 0.1;
} else {
newRadio += 0.1
}
if(newRadio <= 0.1) {
return;
}
this.radio = newRadio;
this.zoom();
},
遇到的问题
对于缩放,我是想做对于鼠标位置放大和缩小,这里要获取到鼠标相对于父元素的坐标(具体做法可以参考上篇文章),保证鼠标放在的那一点相对于父元素的坐标(后都称绝对坐标)不变,再计算中心坐标的偏移量,一开始我是使用矩阵的坐标变换做的,计算量很大,结果显示并不如预期,并且当时矩阵坐标变换也学的不好,我怀疑是算错了,然后突然想到向量,发现用向量的话计算量大大降低,结果显示还是一样。最后还是做的关于中心坐标(包括拖拽后的)的缩放。
对于没能将图形在鼠标位置放大缩小的原因,我认为是做法有问题,项目中的svg图形是以中心点开始向外发散绘制的,所以不应该是鼠标那一点绝对坐标不变,而应该是鼠标所在的元素组的参考点绝对坐标不变。但是实现起来太麻烦,另外如果鼠标没有在任何一个元素组也不好处理,所以干脆以中心坐标不变吧。
另外,此方法简单粗暴,但用处并不太大,只做对拖拽和缩放的理解使用。
最尴尬的是在写这篇博客的时候我看到了一篇文章,让我知道了有transform,汗呐!!!
https://blog.csdn.net/leixu1027/article/details/80519730
总结
- svg计算位置要矩阵和向量灵活使用
- 可以使用transform
这里给出简单代码片段(亲测可用)
let svg = document.getElementById('svg');
svg.style.transform = 'scale(3)';
把svg放大三倍,这里缩放就要计算中心位置偏移量了,然后使用transform的translate进行平移,避免重绘。