Solving Double-Tap Zoom Issues in Touch Devices
This article explores the challenges and solutions for implementing double-tap zoom functionality in touch-enabled devices, addressing common problems with existing methods and proposing a mathematical approach to achieve accurate scaling and panning.
在支持触屏的设备中,我们经常会用到双指缩放的功能。这里我们不讨论双指缩放的触发逻辑,我们假设双指缩放已经发生了,而且我们知道上一次两个触摸点的坐标(p0, p1),以及当前两个触摸点的坐标(p0’,p1’),我们讨论一下缩放的相关参数应该怎么计算。
最常见的方法是,计算上一次两个点(p0, p1)之间的距离d0,以及当前两点(p0’,p1’)之间的距离d1,然后计算缩放值s = d1/d0。我们知道,当我们对一个可视对象进行缩放时,默认是以对象的本地坐标系的坐标原点进行缩放的,所以,我们还要计算缩放的中心点,否则缩放结果并不是我们预期的效果。缩放中心点也很好算,我们可以取当前两个点的中心点作为缩放的中心点,p = (p0’ + p1’) / 2。
然而,这种方法存在问题:
场景1:当固定一个手指,只移动另一个手指时,预期结果是固定手指处不动,但中心点方法会导致移动。
场景2:当两个手指同时移动,保持间距不变时,预期结果是场景跟随手指移动,但中心点方法会导致场景不动。
针对这些问题,常见解法提出:
场景1:以不动的手指为中心缩放。
场景2:提取公共偏移量,减去后计算缩放。
但这些方法只能解决特定场景,无法从根本上解决问题。能不能有一种通用的方案,从根本上解决这个问题?
我们把问题抽离出来。我们现在知道两个点(p0,p1),经过以某个点(p)为中心的缩放(缩放值为s)之后,再经过平移(平移向量为m),变成了点(p0’,p1’)。求p、s、m。
考虑旋转平移缩放,变换矩阵:
只考虑旋转平移缩放,且xy轴等比缩放,不考虑斜切的话,变换矩阵可以变为:
其中 a = s * cos(q),b = s * sin(q),其中s是缩放值,q是旋转角度。tx、ty是平移量。
变换矩阵中有4个未知量。p0->p0’的变换可以列出两个方程,p1->p1’的方程也可以列出两个方程,4个方程,4个未知量,可解。
很好理解,我们把两个手指想象成两个钉子,场景想象成一块橡皮膜,两个钉子扎到橡皮膜上,当移动两个钉子时,橡皮膜会旋转平移缩放,来保证钉子下的两个点是不动的,状态是确定的,一定存在一种变换来满足状态之间的切换。
然而,这并不是我们想要的结果,我们只想要平移和缩放,不想要旋转。
去掉旋转的变换矩阵是这样的:
3个未知数,4个方程,显然无解。但是我们可以求近似解。
以为这样就结束了?
并没有这么简单。直接丢弃旋转是不行的。因为我们上边的变换是以坐标原点为中心的,直接丢弃旋转造成的误差太大了。如果丢弃以实际旋转的中心点为原点的旋转,还是可以接受的。
所以,我们先求出缩放值s:
s = sqrt(a^2 + b^2)
然后求出缩放中心点p:
p = (p0 + p1) / 2
最后求出平移向量m:
m = p0’ - s * (p0 - p)
这样,我们就得到了一个近似的缩放和平移变换。
效果:
总结:
看起来很简单的功能,做好做细也很复杂。任何一个小问题,深入去研究,都很有意思。
关于双指缩放的问题,网上一搜,都是千篇一律的,也都是使用最简单的方式,没有深入分析。
希望上面的分析能起到抛砖引玉的作用。肯定有更简单的解法能得到相同的结果(或者更合理的结果),作者水平有限,只能到这里了。如果大神有更好的解法,望不吝赐教。
New Oriental Technology
Practical internet development experience, tech sharing, knowledge consolidation, and forward-thinking insights.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.