D3 Zoom 原理分析

D3 Zoom

D3 自身有缩放的工具类,D3.zoom(),其会绑定鼠标滚轴的事件进行缩放,但是其本身不会进行缩放,而是需要在自定义的回调函数中进行缩放。使用鼠标进行缩放的时候,默认的缩放发中心为鼠标所在的位置。

参考示例: 《D3 Zoom 固定位置缩放》 《D3 zoom 和 Slider 滑块组件 缩放

Svg transform 和 D3 transform 转换

Svg transform

Svg 中缩放使用 transform 属性进行缩放

1
transform="translate(95 75) scale(1, 1.5) translate(-95 -75)"

transform 属性可以使用多个属性。

1
transform="translate(x1 y1) scale(k1) translate(x2 y2) scale(k2)..."

D3 transform

D3 当然也是使用transform 属性进行SVG元素缩放,但是 D3 使用的 transform 会换算为一个简单格式。

1
transform="translate(X Y) scale(K) "

数据转换

transform 机制参考 《理解SVG坐标系统和变换: transform属性
transform 转换相当于新建坐标体系,不要理解为对原有的数据坐标进行移动。

(1)相连并且相同类型的属性可以直接合并 translate 相加 ,scale 相乘

1
transform="translate(x1 y1) translate(x2 y2) scale(k2) scale(k1)..."

可以直接转换为

1
transform="translate(x1+x2 y1+y2) scale(k2*k1) ..."
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
transform="translate(x1 y1) scale(k1) translate(x2 y2) scale(k2)..."
取用户坐标中的 一点 (A,B)
在移动后的坐标中 A点距离 原点的实际距离为 A*k1*k2
坐标周被移动的距离为 x1 + k1*x2
以上有两个translate ,translate 是参考当前坐标进行移动的,第二个translate 当时的坐标已经放大了 k1,所以移动的实际距离为 x2*k1

同样使用上述算法 取(A,B)一点,坐标轴的缩放最后是一致的
K = k1*k2
transform="translate(X X) scale(K) "
X + AK

X + Ak1*k2 = x1 + k1*x2 + A*k1*k2
X = x1 + k1*x2;
同理
Y = y1 + k1*y2
最后结果为:

transform="translate(x1 + k1*x2 , y1 + k1*y2) scale(k1*k2) "

D3 transform 工具

D3 中 Transform 工具类负责transform的计算

1
transform="translate(x1 y1) scale(k1) translate(x2 y2)

公式为:

1
transform="translate(x1 + k1*x2 , y1 + k1*y2) scale(k1) "

translate API 和上面的计算公式是一致的

1
2
3
translate: function(x, y) {
return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
},
1
transform="translate(x1 y1)scale(k2) scale(k1)"

scale 缩放的时候 直接系数相乘就可以了,参考以上的计算说明

1
2
3
scale: function(k) {
return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
},

常用算法

指定缩放中心缩放

(X,Y) 为缩放中心的坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
transform="translate(X Y) scale(K) translate(-X -Y)"
转换为:
transform="translate(X(1-K) Y(1-K)) scale(K)"

缩放中心算法推到也可以使用,缩放后缩放中心的用户坐标是不变化的。

transform="translate(x y) scale(K) "
设其缩放中心为 (X ,Y)
x + KX = X
x = X-KX = X(1-K)
同理
y = Y(1-Y)

可以叠加使用
transform="translate(X(1-K) Y(1-K)) scale(K) translate(X(1-K) Y(1-K)) scale(K)" 相当于指定中心(X,Y)缩放了KK倍。

增量缩放

1
2
3
4
5
6
7
8
9
10
11
// 以 X Y 为中心缩放K

transform="translate(X(1-K) Y(1-K)) scale(K)"


// 在以上的基础上 再以 A B 为中心缩放 缩放到 K1

transform="translate(X(1-K) Y(1-K)) scale(K) translate(A(1-K1/K) B(1-K1/K)) scale(K1/K)"


transform="translate(X(1-K) + A(K-K1) Y(1-K) + A(K-K1)) scale(K1)"

增量移动

1
2
3
4
5
6
7
8
9
// 以 X Y 为中心缩放K
transform="translate(X(1-K) Y(1-K)) scale(K)"

// 然后移动 A 和 B 的距离
transform="translate(X(1-K) Y(1-K)) scale(K) translate(A/K B/K)"

//转换为 transform="translate(X Y) scale(K)" 格式

transform="translate(X(1-K) + A , Y(1-K) + B) scale(K)"