HTML Canvas — увеличение и уменьшение масштаба точно по указателю мыши с помощью колесика мыши — проверьте пример codepen

Я просто хочу иметь возможность увеличивать и уменьшать масштаб точно на курсоре мыши с помощью колесика мыши. Это должно работать так, как это реализовано в картах Google!

Я сделал пример на codepen, чтобы выделить мою проблему. Пожалуйста, протестируйте его, чтобы увидеть проблему. Просто подведите указатель мыши к левому глазу на изображении. Уменьшите масштаб, затем переместите указатель мыши к правому глазу и снова увеличьте масштаб. Вы увидите, что масштабирование происходит не точно там, где находится мышь.

Анимация с проблемой:

введите сюда описание ссылки

Проблема в том, что я использую ctx.scale для масштабирования изображения и ctx.translate, чтобы вернуть холст в исходное состояние. Это действительно быстрое решение, но оно работает не так точно. Может ли кто-нибудь помочь мне решить эту проблему?

Цель: Чтобы иметь возможность уменьшать и увеличивать масштаб в разных точках изображения, точно так же, как это делается на картах Google с помощью колесика мыши.

Ниже приведена ссылка на мой пример кода, чтобы помочь выделить проблему. Любое решение будет очень признательно!

[https://codepen.io/MKTechStation/pen/abEyBJm?editors=1111][2]

Заранее большое спасибо!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хорошо, я мог найти решение. В основном он состоит в том, чтобы отслеживать все преобразования и сохранять их как инвертированную матрицу. Итак, теперь вычисляется действительно правильное положение мыши относительно преобразований. Пожалуйста, проверьте решение здесь.

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.info("PT: " + `${pt.x}, ${pt.y}`);
    console.info("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());
  };
}

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

Я надеюсь, что это может помочь кому-то. Я все еще открыт для любого другого лучшего решения, чем это!!

solution

Другие вопросы по теме