D3.js 任意位置拖动以及缩放方案


在使用 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,是根据这个改的

缩小->移动->放大截图: 此处输入图片的描述

此处输入图片的描述