在 javascript 中没有 canvas 的情况下缩放至光标

Zoom to cursor without canvas in javascript

我有一个 <img>,它通过调整 transform: scale() 在鼠标滚轮滚动时缩放。我希望缩放就像在 Google 地图中一样,缩放到鼠标光标所在的位置,而不是图像的中心。不过,我不想使用 canvas,只是为了学习经验(这也是为什么我发现的其他问题没有真正帮助的原因)。

我设置了一个JSFiddle来演示问题。我的思考过程是这样的:当放大10%时,图像向四面八方扩展,从图像中心开始,扩展10%。这意味着,例如,左右边缘将在每个方向上移动原始宽度的 5%。因此,我试图像这样解决问题:

  1. 计算鼠标距图像中心的偏移量
  2. 通过将鼠标偏移量乘以缩放因子并除以二来计算新的图像偏移量(顶部和左侧)
  3. 应用偏移,看着它用一百万个燃烧的太阳的力量炸毁我的脸

看来我就是找不到合适的公式或算法。

你可以使用 transform-origin : <position of your mouse pointer> :

  • transform-origin : 0% 0%点在左上角。
  • transform-origin : 100% 100%点在右下角。

这是我做的一个例子:https://jsfiddle.net/zez538L8/4/

javaScript :

var currentzoom = 1;

function zoom(delta, e) {
   var img = document.getElementById("test");
   var width = img.offsetWidth; //calculating the size of the img (in px)
   var height = img.offsetHeight;
   var x = event.offsetX; //calculating the position of the mouse pointer on the picture (in px)
   var y = event.offsetY; 
   var xpercent = x*100/width; //calculating the position of the mouse pointer on the picture (in %)
   var ypercent = y*100/height;
   img.style.transform = "scale("+currentzoom+")"; //scaling the picture
   img.style.transformOrigin = xpercent + "% "+ ypercent +"%"; //transform-origin
   currentzoom += delta;
}

最终我自己弄明白了,尽管只是通过查看现有的解决方案。这是仅包含要点的JSFiddle

思路是先设置transform-origin: 0 0。这确保了在缩放时图像会向下和向右扩展,而不是将宽度的增加分布在所有四个边上。请注意,它不会重新定位图像,它只是 changes the origin for all transformations.

此外,此 JSFiddle 假定图像的上边缘和左边缘与容器元素的上边缘和左边缘对齐。如果图像应该在缩放之前重新定位,这应该通过 transform: translate() 完成,并且 translateXtranslateY 值需要相应地更新。

逻辑的核心是:

  // Track the percentage change between the old
  // and the new scale of the image
  const ratio = 1 - nextScale / currentScale

  // get the current mouse offset
  const {
    clientX,
    clientY
  } = event

  // The += here is extremely important!
  // The new 2D translation values are derived from the difference
  // between mouse cursor position and current (!) 2D translation.
  // So where is the mouse cursor relative to the translated image
  // This difference is then adjusted by the % change of the scaling
  translateX += (clientX - translateX) * ratio
  translateY += (clientY - translateY) * ratio

  /*
  This would work for the first wheel scroll. But afterwards, the 
  image will not be translated enough to offset the zooming because 
  we're not taking into account the existing translation
  translateX += (clientX - translateX) * ratio
  translateY += (clientY - translateY) * ratio
  */

所以总结一下所需的步骤:

  1. 计算下一个尺度
  2. 计算当前鼠标相对于翻译图像的偏移量
  3. 根据缩放比例的变化调整鼠标偏移量,例如const percentChange = 1 - nextScale / currentScale
  4. 将调整后的鼠标偏移添加到 translate()
  5. 的现有值
  6. 应用转换(缩放和平移)

链接的 JSFiddle 还包括 Lodash 和 transition: transform 330ms ease-in-out; 以使滚动更平滑并且不会对浏览器性能造成太大影响。