以鼠标位置为中心缩放实现原理与细节

背景

公司在推行降本增效,在该过程中需要自研一个类似于蓝湖的设计平台,其中详情页现有的缩放能力是以图片中心进行缩放的,无论鼠标位置在哪都以图片中心进行缩放,这是不符合我们的预期的。

思路

原理

以鼠标位置为中心点进行缩放其实就是先进行缩放然后再进行平移操作,将缩放完成的图形平移平移到鼠标位置,使得缩放前后鼠标指向的图形位置不变。

有了思路我们就可以实现了,首先我们需要先确定我们的图形是以什么位置进行缩放,这点很重要,因为不同的缩放位置进行平移的时候算法不同。

以图形左上角进行缩放后再平移

  • 其中绿色为我们缩放前的图形,黄色为我们缩放后的图像,其中我们的鼠标位置为(clientX,clientY),缩放完成后我们的鼠标位置是不变的,但是我们在缩放前指的图形上的点此时已经到了(xn+x0,yn+y0)上去,我们要做的就是求解上图中的△x与△y,我们这里以求解△x为例,只要求解到该值然后将缩放后的图形进行偏移即可。

  • 我们这里先声明一下上图中的含义:

    • (x0,y0)为缩放前后的图像左上角顶点
    • x1、y1分别表示缩放后的图形的宽高
    • n表示缩放前后的倍率
  • 因此我们可以得出对应关系如下:

    • △x = x * n + x0 - (x + x0) => x * n - x => x * (n - 1)
    • clientX = x0 + x => x = clientX - x0
  • 将上述两式进行联合可得:

    • △x = (clientX - x0) * (n - 1)
  • 其中clientX为鼠标的位置为已知,x0为缩放前的位置也为已知,n为缩放前后的倍率,该倍率可被简单求解:

    • 如缩放前的图形为原图的0.5倍,缩放后的为原图的2倍则倍率为2 / 0.5 = 4
  • △x证明如上,△y同理可证,在此不做赘述。

  • 因此我们的△x与△y均已变为已知,仅需在缩放后进行偏移即可。

矩阵拓展

我们知道了原理后可以进行实践,在此我们还需要知道在css中的transform是如何运作的。

  • 例如我们知道了先进行缩放在进行平移可能顺手就写下了 transform: scale(s) translate(x,y)这行代码,看上去也没什么问题。但是当我们使用该行代码时问题出现了,我们发现缩放的大小是正常的,但是平移的尺寸不正常,像是先进行了平移然后进行的缩放,跟我们书写的顺序不符合

  • 此时我们就需要深究一下transform具体是怎么运作的了,我们在大学学过线性代数,网页的css图形的变换其实就是线性代数中的矩阵变换。因为2d的图形是一个平面,而平面是由点构成的,我们只需要让每个点进行相对应的矩阵变换,到其应该到的位置上去那么我们的整个图形也就进行了变换。

  • 当然我们如果只是为了解决上述问题我们可以将 transform: scale(s) translate(x,y)替换为transform: translate(x,y) scale(s) 或者替换为matrix(scaleX,skewY,skewX,scaleY,translateX,translateY)矩阵函数。其中该矩阵函数接受六个参数,分别是x轴缩放倍数、y轴倾斜角度、x轴倾斜角度、y轴缩放倍数、x轴偏移量、y轴偏移量。那么为什么替换成这个矩阵函数后就正常了呢?

    • 因为我们写的 translate(x,y) scale(s) 等参数最后都会经过计算变为矩阵函数,我们直接写矩阵函数相当于一步到位了。

    • 既然知道了原理那么我们可以探究一下为什么跟写的顺序有关系,因为我们的translate(x,y) scale(s)会首先转换成一个3 * 3的矩阵至于为什么是3 * 3的矩阵原因如下:

      • 我们的矩阵函数有6个参数,最少需要23或32

      • 矩阵相乘第一个矩阵的列数必须等于第二个矩阵的行数。

      • 相乘后的矩阵的行数等于第一个矩阵的行数,列数等于第二个矩阵的列数。

      • 相乘后的矩阵的元素等于第一个矩阵的行的元素与第二个矩阵列的对应元素的积之和。

      • 矩阵相乘规则如下:

        • 针对某个点(x,y)应用矩阵变换后得到的(x,y) x= ax +cy+e y= bx + dy + f
    • 只有满足了以上的条件我们的矩阵才可以进行相乘,因此为了构造矩阵构造成为3*3。

    • 我们利用上面的矩阵相乘可以得到如下:

      • 其中矩阵的第三行是我们补充的行数,忽略即可。
      • 矩阵第一行表示x轴缩放倍数、x轴倾斜角度、x轴偏移量。
      • 矩阵第二行表示y轴倾斜角度、y轴缩放倍数、y轴偏移量。
    • 因此矩阵相乘效果如下:

    • 可以看出我们先写缩放参数与先写平移参数计算出来是不同的,原因是矩阵的乘法不满足简单的乘法交换律。

  • 至此我们已经了解了为什么transform的参数书写顺序会与我们的感知有差别了,因此我们可以直接使用矩阵函数来规避该差别或是将平移放在transform参数的前面来规避因缩放引起的平移距离与我们的感知问题。

以图形中心进行缩放后再平移

上述介绍了以选定缩放点为左上角时以鼠标位置为缩放中心点的实现,接下来我们拓展一下以默认缩放点也就是图形的中心为缩放点的时候如何以鼠标位置为缩放中心点。

  • 如图所示,其中黄色为我们缩放前的图形,绿色为我们缩放后的图形,我们的鼠标位置为(clientX,clientY),缩放完成后我们鼠标在缩放前指的位置已经到了(x2,y2)区域,因此我们要做的还是缩放完成后将图形进行偏移△x与△y使其(x2,y2)与(clientX,clientY)两点重合。

  • 我们仍然是先声明一下上图中的含义:

    • (x0,y0)为缩放前的图像左上角顶点
    • x1分别表示缩放后的图形的宽, x为缩放前的宽
    • n表示缩放前后的倍率
  • 我们仍然可以得出对应关系如下:

    • x1 = x * n

    • △x = (x - x1) / 2 +( x2 - x`)-(clientX - x0)

      • (x - x1) / 2表示原图形减去缩放后的图像变化的宽度的一半也就是黄色图形的左侧与绿色图形的左侧的距离
      • x2-x`表示缩放后的鼠标应该指的位置与缩放后的图形的左边距的距离
      • 上述两项之和就是缩放前的图形的左侧到缩放后的图像的鼠标指的位置的距离
      • (clientX - x0)表示鼠标位置距离缩放前图形的左侧的距离,不难得出如上的△x求解公式
    • 联合上述两式,(x - x1) / 2 = (x - x * n)/2 = (1 - n)*x / 2

    • x2 - x` = (clientX - x0) * n

    • △x = (1 -n )* x / 2 + (clientX - x0) * n - (clientX- x0)

    • △x = (1 -n )* x / 2 - (clientX - x0) * (1 - n)

    • △x = (1 -n )* (x / 2 - clientX + x0 )

△y与△x求解同理,至此我们已经可以先以图形中心进行缩放然后再进行平移了~

相关推荐
正小安1 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch3 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光3 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   3 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   3 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web3 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常3 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇4 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr4 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho5 小时前
【TypeScript】知识点梳理(三)
前端·typescript