在使用 D3 的时候可能有的场景需要任意拖动,也有的可能需要任意缩放 这两个功能单独满足的话都不会是问题,但是同时满足就需要琢磨下了
PS:看好了,这里的任意位置意思是可以为负值区域,即任意象限! PPS:这篇文章是假设你对 svg 的 ViewBox 是了解的,且思考过缩放拖动问题
任意拖动
先来看几个 D3 的事件:mousedown/mouseup/mousemove 看名字就知道干嘛的 :-) 要实现任意拖动,流程应该如下:
- 在 mousedown 的一瞬间记录此时坐标: mousePos_x 和 mousePos_y
- 在 mousemove 的时候判断是否是 mousedown 状态,如果是则通过当前坐标以及 mousedown 的时候的坐标,计算平移量,设置对应的 ViewBox
- (为了规避误差以及标记用)在 mouseup 的时候,也需要计算 ViewBox 并且设置
然后代码片段大概就这样:
svg.on("mousedown", function () {
isMouseDown = true;
mousePos_x = d3.mouse(this)[0];
mousePos_y = d3.mouse(this)[1];
});
svg.on("mouseup", function () {
isMouseDown = false;
viewBox_x = viewBox_x - d3.mouse(this)[0] + mousePos_x;
viewBox_y = viewBox_y - d3.mouse(this)[1] + mousePos_y;
svg.attr("viewBox", viewBox_x + " " + viewBox_y + " " + width / oldScale + " " + height / oldScale);
});
svg.on("mousemove", function () {
curPos_x = d3.mouse(this)[0];
curPos_y = d3.mouse(this)[1];
if (isMouseDown) {
viewBox_x = viewBox_x - d3.mouse(this)[0] + mousePos_x;
viewBox_y = viewBox_y - d3.mouse(this)[1] + mousePos_y;
svg.attr("viewBox", viewBox_x + " " + viewBox_y + " " + width / oldScale + " " + height / oldScale);
}
});
至于 curPos_ 以及 oldScale 是用于任意缩放的
任意缩放
D3 提供了 zoom 事件,鼠标中键或者双击的时候都会触发,回调里会提供此时的 scale 供使用
假设没有拖动操作,缩放还是比较简单,大概会是一个小学应用题(相似比):
x0/(x0-x) = y0/(y0-y) = scale
解出来设置 ViewBox 即可
但是有了平移后,就会出现负值区域,无法直接用相似计算
可以转换下思想,从 ViewBox 的区域根据相似计算
但是前提是会有个平移量转化,用公式大概会是:oldScale/newScale = (x0-x)/(x0-x1) = (y0-y)/(y0-y1)
其中 x1/y1 为当前 ViewBox 的起始坐标点
然后大概代码片段就会像这样:
svg.call(d3.behavior.zoom()
.scaleExtent([0.1, 10])
.on("zoom", function () {
if (oldScale !== d3.event.scale) {
var scale = oldScale / d3.event.scale;
oldScale = d3.event.scale;
viewBox_x = curPos_x - scale * (curPos_x - viewBox_x);
viewBox_y = curPos_y - scale * (curPos_y - viewBox_y);
svg.attr("viewBox", viewBox_x + " " + viewBox_y + " " + width / oldScale + " " + height / oldScale);
}
}));
演示
如果上面说的还是没弄明白,可以参考我的 DEMO,是根据这个改的
缩小->移动->放大截图: