<commit_before>---
layout:     post
title:      D3.js 任意位置拖动以及缩放方案
date:       2015-06-09 11:48:00
summary:    在使用 D3 的时候可能有的场景需要任意拖动，也有的可能需要任意缩放 
这两个功能单独满足的话都不会是问题，但是同时满足就需要琢磨下了
tags:       前端
---


在使用 D3 的时候可能有的场景需要任意拖动，也有的可能需要任意缩放
这两个功能单独满足的话都不会是问题，但是同时满足就需要琢磨下了

*PS：看好了，这里的任意位置意思是可以为负值区域，即任意象限！*
*PPS：这篇文章是假设你对 svg 的 ViewBox 是了解的，且思考过缩放拖动问题*

任意拖动
===

先来看几个 D3 的事件：mousedown/mouseup/mousemove
看名字就知道干嘛的 :-)
要实现任意拖动，流程应该如下：

 - 在 mousedown 的一瞬间记录此时坐标： mousePos_x 和 mousePos_y
 - 在 mousemove 的时候判断是否是 mousedown 状态，如果是则通过当前坐标以及 mousedown 的时候的坐标，计算平移量，设置对应的 ViewBox
 - （为了规避误差以及标记用）在 mouseup 的时候，也需要计算 ViewBox 并且设置
 
然后代码片段大概就这样：

```javascript
  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 的起始坐标点

然后大概代码片段就会像这样：

```javascript
    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][1]，是根据[这个][2]改的

缩小->移动->放大截图：
![此处输入图片的描述][3]

![此处输入图片的描述][4]


  [1]: http://7xjl4v.com1.z0.glb.clouddn.com/index.html
  [2]: http://bl.ocks.org/mbostock/4062045
  [3]: http://int64ago.qiniudn.com/tvm9mxrxgk3xr.png
  [4]: http://int64ago.qiniudn.com/lfmggcxv9rudi.png<commit_msg>Fix post: new line in summary<commit_after>---
layout:     post
title:      D3.js 任意位置拖动以及缩放方案
date:       2015-06-09 11:48:00
summary:    在使用 D3 的时候可能有的场景需要任意拖动，也有的可能需要任意缩放，这两个功能单独满足的话都不会是问题，但是同时满足就需要琢磨下了
tags:       前端
---


在使用 D3 的时候可能有的场景需要任意拖动，也有的可能需要任意缩放
这两个功能单独满足的话都不会是问题，但是同时满足就需要琢磨下了

*PS：看好了，这里的任意位置意思是可以为负值区域，即任意象限！*
*PPS：这篇文章是假设你对 svg 的 ViewBox 是了解的，且思考过缩放拖动问题*

任意拖动
===

先来看几个 D3 的事件：mousedown/mouseup/mousemove
看名字就知道干嘛的 :-)
要实现任意拖动，流程应该如下：

 - 在 mousedown 的一瞬间记录此时坐标： mousePos_x 和 mousePos_y
 - 在 mousemove 的时候判断是否是 mousedown 状态，如果是则通过当前坐标以及 mousedown 的时候的坐标，计算平移量，设置对应的 ViewBox
 - （为了规避误差以及标记用）在 mouseup 的时候，也需要计算 ViewBox 并且设置
 
然后代码片段大概就这样：

```javascript
  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 的起始坐标点

然后大概代码片段就会像这样：

```javascript
    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][1]，是根据[这个][2]改的

缩小->移动->放大截图：
![此处输入图片的描述][3]

![此处输入图片的描述][4]


  [1]: http://7xjl4v.com1.z0.glb.clouddn.com/index.html
  [2]: http://bl.ocks.org/mbostock/4062045
  [3]: http://int64ago.qiniudn.com/tvm9mxrxgk3xr.png
  [4]: http://int64ago.qiniudn.com/lfmggcxv9rudi.png
{'fraction_non_alphanumeric': 0.1464888888888889, 'fraction_numerical': 0.0288, 'mean_word_length': 2.6203346203346203, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '980408', 'n_tokens_mistral': 3042, 'n_tokens_neox': 3181, 'n_words': 365}