在一些大屏编辑场景下,需要实现缩放功能。采用 transform: scale 来实现后,发现原来相对定位准确的两个元素在缩放后发生了偏移。这时需要重新计算相应的 top/left 值,来适应不同的 scale 参数。

绝对定位下的缩放

看这样一个例子,直接对最外层的 .outer 做 scale,里面两个 box 的相对定位是没有问题的。

不过,这时候灰色的 .outer 自身也被缩放了。这个 outer 在这个场景中承担的是一个类似于「画布容器」的作用,里面的 .box1 才是真正的「画布」,另外我们也希望这个画布会有一些可插拔的插件系统,比如参考线之类的,这个我们用 .box2 来体现。

为了方便下文的说明,注意这里 box2 的下边与右边均与 box1 贴紧。

现在我们希望只缩放里面的两个 box,而 outer 自身维持不变,看看效果:

容器没有缩放了,但仔细一看两个 box 之间的相对位置发生了改变,两个 box 的右下两边不再贴紧了。实际场景中,这个只是一个缩放功能,缩放之后画布与插件发生了偏移,这当然是不能接受的。

把原来的虚线框显示出来后发现是因为两个 box 的 scale 缩放原点是他们各自的中心(transform-origin 属性),因此原来的两个 box 的相对位置就发生的改变。既然知道了原因,我们就可以通过动态调整每个 box 的 left/top 值来使得每个缩放比例下,两个 box 之间的相对位置维持一致。

解方程式

由于 x 方向和 y 方向上是独立一致的,我们只做一遍 x 方向上的计算,y 方向直接套结论就好了。先来一张示意图。

我们写在 CSS 上的是蓝色的原来的 left 值,而为了实现的目标则是红色的缩放后的 left 值是实际 left 值的缩放后结果。下面先引入一堆变量:

$$\text{box1原来的宽度:}width_1=500$$

$$\text{box2原来的宽度:}width_2=470$$

$$\text{box2相对与box1的定位:}left=30$$

$$\text{缩放因子:}scale$$

ok,引入这堆变量后就可以列方程了哈哈哈,设这个要求的蓝色 left 值为 $x$,这个 $x$ 的计算方法就是两段黑色线段之差,而其中较长的黑色线段长度与 box1 缩放相关:

$$x = Black_1 - Black_2 = \left(\frac{width_1 - width_1 \cdot scale}2\right) - Black_2$$

而这个 $Black_2$ 则可以直接关联到红色线段和 box2 的缩放!红色线段的目标是等比例的缩放相对定位,那么:

$$Black_2 = Black_3 - Red = \left(\frac{width_2 - width_2 \cdot scale}2\right) - left \cdot scale$$

完美,综合上述两式,稍微推倒一下:

$$x = \left(\frac{width_1 - width_1 \cdot scale}2\right) - \left(\frac{width_2 - width_2 \cdot scale}2\right) + left \cdot scale$$

$$x = \frac{(width_1 - width_2)\cdot(1 - scale)}2 + left \cdot scale$$

同理可以得到垂直方向的计算公式

$$y = \frac{(height_1 - height_2)\cdot(1 - scale)}2 + top \cdot scale$$

代入 $scale=0.6$ 计算一下得出 $x = y = 24$

所以当 scale 为 0.6 时,原来的 top / left 调整到 24px 就能让缩放后的盒子维持相对位置了。

以上。