Javascript и холст, проблема с координатами вращения изображения

У меня проблема с повернутыми координатами изображения. Когда я нажимаю правую кнопку мыши на изображении, в месте щелчка мыши должен появиться желтый кружок. Он отлично работает с начальным изображением, а также с изображением с измененным размером (колесо мыши) и перетаскиваемым изображением (щелкните левой кнопкой мыши и перетащите). Проблема в том, что я не могу правильно рассчитать правильные координаты мыши после поворота изображения с помощью ползунка - например, когда я поворачиваю изображение на 180 градусов, круг появляется по диагонали, но он должен появиться в том месте, где я щелкнул. Интересно то, что когда я помещаю круги перед вращением, а затем поворачиваю изображение, круги вращаются с правильными координатами. В моем коде не хватает какого-то преобразования, но я просто не могу понять, какое именно и куда его вставить. Буду признателен за помощь. Заранее спасибо;)

Вот мой код:

window.onload = () => {
  const canvas = document.getElementById("canvas");
  const context = canvas.getContext("2d");
  context.imageSmoothingEnabled = false;
  const mousePos = document.getElementById("mouse-pos");
  const transformedMousePos = document.getElementById("transformed-mouse-pos");

  const image = new Image();
  image.src =
    "https://e7.pngegg.com/pngimages/584/916/png-clipart-bright-blue-sky-aqua-blue-button-rounded-blank-download-free-download-png-download-vector-download-svg-download-transparent-thumbnail.png";
  image.onload = drawImageToCanvas;

  let isDragging = false;
  let dragStartPosition = { x: 0, y: 0 };
  let currentTransformedCursor;

  class Obraz {
    constructor(x, y) {
      this.x = x;
      this.y = y;
    }
  }

  let points = [];

  function markPoint(x, y) {
    let obraz = new Obraz(x, y);
    points.push(obraz);
  }

  function drawPoints() {
    const centerX = canvas.width / 2;
    const centerY = canvas.height / 2;

    context.save();
    context.translate(centerX, centerY);
    context.rotate((rotationAngle * Math.PI) / 180);
    const radius = 10;
    context.lineWidth = 3;
    context.strokeStyle = "#FFFF00";
    context.filter = "none";
    points.forEach((item) => {
      context.beginPath();
      context.arc(
        item.x - centerX,
        item.y - centerY,
        radius,
        0,
        2 * Math.PI,
        false
      );
      context.stroke();
    });
    context.restore();
  }

  function drawImageToCanvas() {
    context.save();
    context.setTransform(1, 0, 0, 1, 0, 0);
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.restore();

    context.save();
    context.translate(canvas.width / 2, canvas.height / 2);
    context.rotate((rotationAngle * Math.PI) / 180);

    context.drawImage(image, -100, -100, 200, 200);
    context.restore();

    drawPoints();
  }

  let rotateSlider = document.getElementById("rotateSlider");
  let rotationAngle = 0;

  rotateSlider.addEventListener("input", function () {
    rotationAngle = this.value;
    drawImageToCanvas(rotationAngle);
  });

  function getTransformedPoint(x, y) {
    const originalPoint = new DOMPoint(x, y);
    return context.getTransform().invertSelf().transformPoint(originalPoint);
  }

  function onMouseDown(event) {
    isDragging = true;
    dragStartPosition = getTransformedPoint(event.offsetX, event.offsetY);

    if (event.button === 2) {
      currentTransformedCursor = getTransformedPoint(
        event.offsetX,
        event.offsetY
      );
      markPoint(currentTransformedCursor.x, currentTransformedCursor.y);

      drawImageToCanvas();
    }
  }

  function onMouseMove(event) {
    currentTransformedCursor = getTransformedPoint(
      event.offsetX,
      event.offsetY
    );
    mousePos.innerText = `Original X: ${event.offsetX}, Y: ${event.offsetY}`;
    transformedMousePos.innerText = `Transformed X: ${currentTransformedCursor.x}, Y: ${currentTransformedCursor.y}`;

    if (isDragging) {
      context.translate(
        currentTransformedCursor.x - dragStartPosition.x,
        currentTransformedCursor.y - dragStartPosition.y
      );
      drawImageToCanvas();
    }
  }

  function onMouseUp() {
    isDragging = false;
  }

  function onWheel(event) {
    const zoom = event.deltaY < 0 ? 1.1 : 0.9;

    context.translate(currentTransformedCursor.x, currentTransformedCursor.y);
    context.scale(zoom, zoom);
    context.translate(-currentTransformedCursor.x, -currentTransformedCursor.y);

    drawImageToCanvas();
    event.preventDefault();
  }

  let buttonReset = document.getElementById("reset");

  buttonReset.addEventListener("click", function (event) {
    rotateSlider.value = 0;
    rotationAngle = 0;
    drawImageToCanvas();
  });

  let buttonClear = document.getElementById("clear");

  buttonClear.addEventListener("click", function (event) {
    points = [];
    drawImageToCanvas();
  });

  canvas.addEventListener("mousedown", onMouseDown);
  canvas.addEventListener("mousemove", onMouseMove);
  canvas.addEventListener("mouseup", onMouseUp);
  canvas.addEventListener("wheel", onWheel);

  canvas.addEventListener("contextmenu", function (ev) {
    ev.preventDefault();
  });
};
.nav-header {
    font-size: 1.5rem;
}
 
.row {
    margin-bottom: 0;
}
 
#sourceImage,
.image-controls,
.image-save,
.preset-filters {
    display: none;
}
 
.image-preview {
    display: flex;
    justify-content: center;
    margin-top: 20px;
}
 
#canvas {
    max-height: 420px;
    object-fit: contain;
}

.container-image {
    overflow: auto;
    justify-content: center;
    align-items: center;

    width: 90%;
    height: 90%;

    position: absolute;
}

.image {
    object-fit: cover;
    transform: translate(20%, 20%);
}
<canvas id = "canvas" width = "350" height = "350"></canvas>

<div class = "col s6">
  <span class = "range-field">
    <input id = "rotateSlider" type = "range" value = "0" min = "-180" max = "180">
  </span>
  <button class = "btn btn-flat red white-text" id = "reset">
    Reset
  </button>
  <button class = "btn btn-flat red white-text" id = "clear">
    Clear
  </button>
</div>

<div id = "mouse-pos"></div>
<div id = "transformed-mouse-pos"></div>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Я нашел решение проблемы, требующее «слегка» модифицировать преобразования методом щелчка. Я оставлю эту тему на случай, если кто-то придумает подобное решение.

function onMouseDown(event) {
    isDragging = true;
    dragStartPosition = getTransformedPoint(event.offsetX, event.offsetY);
    
    if (event.button === 2) {
        currentTransformedCursor = getTransformedPoint(event.offsetX, event.offsetY);
        const transformedX = currentTransformedCursor.x - canvas.width / 2;
        const transformedY = currentTransformedCursor.y - canvas.height / 2;
        const unrotatedX = Math.cos(-rotationAngle * Math.PI / 180) * transformedX - Math.sin(-rotationAngle * Math.PI / 180) * transformedY + canvas.width / 2;
        const unrotatedY = Math.sin(-rotationAngle * Math.PI / 180) * transformedX + Math.cos(-rotationAngle * Math.PI / 180) * transformedY + canvas.height / 2;
        markPoint(unrotatedX, unrotatedY);
        drawImageToCanvas();
    }
}

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