HTML Canvas - 使用鼠标滚轮精确地在鼠标指针上放大和缩小 - 检查 codepen 示例

Tur*_*les 2 mouse scroll canvas zooming

我只是希望能够使用鼠标滚轮精确地放大和缩小鼠标光标。它应该像在谷歌地图中实现的那样工作!

我在 codepen 上做了一个例子来突出我的问题。请测试一下看看问题所在。只需将鼠标指针移至图像中的左眼即可。缩小很多,然后将鼠标指针移动到右眼并再次放大。您会发现它并没有精确地缩放到鼠标位置。

有问题的动画:

在此输入链接描述

问题是我使用 ctx.scale 来缩放图像,并使用 ctx.translate 使画布恢复到原始状态。这是一个非常快的解决方案,但它的工作并不那么精确。有人可以帮我解决这个问题吗?

目标: 能够在图像的不同点进行缩小和缩小,就像谷歌地图使用鼠标滚轮所做的那样。

下面是我的代码示例的链接,以帮助突出问题。任何解决方案将非常感激!

[https://codepen.io/MKTechStation/pen/abEyBJm?editors=1111][2]
Run Code Online (Sandbox Code Playgroud)

预先非常感谢您!

Tur*_*les 5

好的,我可以找到解决方案。它基本上包括跟踪所有变换并将其保存为倒矩阵。因此,现在根据变换计算出真正正确的鼠标位置。请在此处检查解决方案。

window.onload = function () {
  const img = document.querySelector("img");
  const canvas = document.querySelector("canvas");
  const ctx = canvas.getContext("2d");
  trackTransforms(ctx);

  let zoom = 1;
  let mousePos = {
    x: 0,
    y: 0
  };

  draw();
  document.body.addEventListener("mousemove", (e) => {
    const canvas = document.querySelector("canvas");
    const rect = canvas.getBoundingClientRect();
    mousePos.x = e.clientX - rect.left;
    mousePos.y = e.clientY - rect.top;
  });

  document.getElementById("canvas").addEventListener("wheel", (e) => {
    e.preventDefault();
    zoom = e.deltaY < 0 ? 1.1 : 0.9;
    scaleDraw();
    draw();
  });

  function draw() {
    ctx.save();
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.restore();
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  }

  function scaleDraw() {
    const pt = ctx.transformedPoint(mousePos.x, mousePos.y);
    console.log("PT: " + `${pt.x}, ${pt.y}`);
    console.log("MOUSEPOINTER: " + `${mousePos.x}, ${mousePos.y}`);
    ctx.translate(pt.x, pt.y);
    ctx.scale(zoom, zoom);
    ctx.translate(-pt.x, -pt.y);
  }
};

function trackTransforms(ctx) {
  debugger;
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  let xform = svg.createSVGMatrix();

  const savedTransforms = [];
  let save = ctx.save;
  ctx.save = () => {
    savedTransforms.push(xform.translate(0, 0));
    return save.call(ctx);
  };

  let restore = ctx.restore;
  ctx.restore = () => {
    xform = savedTransforms.pop();
    return restore.call(ctx);
  };

  let scale = ctx.scale;
  ctx.scale = (sx, sy) => {
    xform = xform.scaleNonUniform(sx, sy);
    return scale.call(ctx, sx, sy);
  };

  let translate = ctx.translate;
  ctx.translate = function (dx, dy) {
    xform = xform.translate(dx, dy);
    return translate.call(ctx, dx, dy);
  };

  let pt = svg.createSVGPoint();
  ctx.transformedPoint = (x, y) => {
    pt.x = x;
    pt.y = y;
    return pt.matrixTransform(xform.inverse());
  };
}
Run Code Online (Sandbox Code Playgroud)

https://codepen.io/MKTechStation/pen/pobbKOj

我希望它可以帮助别人。我仍然愿意接受任何其他更好的解决方案!

解决方案