Применение масштабирования к точке, например Aseprite

Здравствуйте, мне нужна помощь с применением ответа на другой вопрос к моему коду. В настоящее время я пытаюсь воссоздать функцию масштабирования aseprite, но с моим текущим кодом, когда я масштабирую разные стороны холста, он смещается на пару пикселей.

Ранее мне сказали, что мой вопрос дублирует: Увеличьте точку (используя масштаб и перевод)

И я посмотрел: https://stackoverflow.com/a/3151987/24209432, но, похоже, не могу заставить их ответ работать с моим кодом. Пожалуйста, помогите мне с этим.

Вот гифка, показывающая, что происходит в данный момент: Проблема со сдвигом масштаба

А вот как это должно работать: Aseprite Zoom

Вот мой соответствующий код:

<html>
  <body>
    <main>
      <section Id = "canvas-panel">
        <section
          id = "canvas"
          style = "width: 256px; height: 256px; background-size: 12.5%"
        >
          <canvas width = "256px" height = "256px"></canvas>
        </section>
      </section>
    </main>
  </body>
</html>

<script>
  let canvasPanel = document.getElementById("canvas-panel");
  let canvas = document.getElementById("canvas");

  // Function to calculate the closest point on the border of the canvas to the click position.
  function calculateClosestBorderPoint(event) {
    const canvasRect = canvas.getBoundingClientRect();

    const clickX = event.clientX;
    const clickY = event.clientY;

    let closestX = canvasRect.left;
    let closestY = canvasRect.top;

    // Determine if the click is closer to the left or right side of the canvas.
    if (clickX < canvasRect.left) {
      closestX = canvasRect.left;
    } else if (clickX > canvasRect.right) {
      closestX = canvasRect.right;
    } else {
      closestX = clickX;
    }

    // Determine if the click is closer to the top or bottom of the canvas.
    if (clickY < canvasRect.top) {
      closestY = canvasRect.top;
    } else if (clickY > canvasRect.bottom) {
      closestY = canvasRect.bottom;
    } else {
      closestY = clickY;
    }

    return { x: closestX, y: closestY };
  }

  // Initialize scale.
  let scale = 1;

  // Define zoom limits
  const minScale = 0.5; // Minimum zoom limit.
  const maxScale = 4.0; // Maximum zoom limit.

  // Function to update the canvas transformation.
  function updateCanvasTransform() {
    canvas.style.transform = `scale(${scale})`;
  }

  canvasPanel.addEventListener("wheel", function (e) {
    e.preventDefault();

    const zoomDirection = e.deltaY < 0 ? 1 : -1;

    if (scale == minScale && zoomDirection == -1) return;
    if (scale == maxScale && zoomDirection == 1) return;

    const zoomFactor = 1.1;

    const closestPoint = calculateClosestBorderPoint(event);

    scale *= zoomFactor ** zoomDirection; // Apply zoom factor based on direction

    // Clamp scale to limits
    scale = Math.min(scale, maxScale);
    scale = Math.max(scale, minScale);

    // Update canvas position to keep the closest point centered relative to the viewport
    const canvasRect = canvas.getBoundingClientRect();
    const viewportCenterX = canvasRect.width / 2;
    const viewportCenterY = canvasRect.height / 2;

    // Convert closest point to relative position within the viewport (0-1 scale)
    let relativeX =
      ((closestPoint.x - canvasRect.left) / canvasRect.width) * 100;
    let relativeY =
      ((closestPoint.y - canvasRect.top) / canvasRect.height) * 100;

    canvas.style.transformOrigin = `${relativeX}% ${relativeY}%`;

    // Update canvas transformation using only scale
    updateCanvasTransform();
  });
</script>

<style>
  #canvas {
    position: fixed;
    overflow: hidden;
    padding: 0;
    margin: 0;
    transform: scale(1) translate(0px, 0px);
    background-color: #1d1d1d;
  }

  #canvas-panel {
    position: relative;
    width: 100%;
    height: 100%;
    background-color: #303030;
  }

  html,
  body {
    background-color: #161616;
    margin: 0;
    width: 100%;
    height: 100%;
  }
</style>

Отвечает ли это на ваш вопрос? Увеличьте точку (используйте масштаб и переведите)

anmabe 30.04.2024 13:10

@anmabe Нет, как указано в моем вопросе, это близко, но мне нужно добавить его для моего кода. Математика не моя сильная сторона, извините.

RandomSkinCreator 30.04.2024 13:13

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

anmabe 30.04.2024 13:15

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

RandomSkinCreator 30.04.2024 14:12
Поведение ключевого слова "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) для оценки ваших знаний,...
2
4
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Это сработало, удачи людям, которые найдут это позже! :)

<script src = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id = "canvas-panel" onclick = "calculateClosestBorderPoint(event)">
  <div id = "canvas"></div>
</div>

<style>
  body {
    background-color: orange;
  }
  #canvas-panel {
    margin: 0;
    width: 800px;
    height: 800px;
    background-color: white;
    position: relative;
    overflow: hidden;
  }
  #canvas {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    background-color: red;
    width: 600px;
    height: 600px;
  }
</style>

<script>
  const MIN_SCALE = 0.2;
  const MAX_SCALE = 10;
  const zoomFactor = 1.2;

  var scale = 1;

  var offsetX = 0;
  var offsetY = 0;

  var $canvas = $("#canvas");
  var $canvasPanel = $("#canvas-panel");

  var canvasPanelWidth = $canvasPanel.width();
  var canvasPanelHeight = $canvasPanel.height();

  var canvasWidth = $canvas.width();
  var canvasHeight = $canvas.height();
  
  $(document).ready(setMargin);

  // Set the margin of the canvas for when the canvas is bigger than the canvasPanel.
  // This is all because margin: auto; doesn't work when the canvas is bigger than the panel.
  function setMargin() {
    var marginX = (canvasPanelWidth - canvasWidth) / 2;
    var marginY = (canvasPanelHeight - canvasHeight) / 2;
    $canvas.css('margin', `${marginY} ${marginX}`);
}

  function calculateClosestBorderPoint(rect, x, y) {
    const clickX = x;
    const clickY = y;

    let closestX = rect.left;
    let closestY = rect.top;

    // Determine if the click is closer to the left or right side of the canvas.
    if (clickX < rect.left) {
      closestX = rect.left;
    } else if (clickX > rect.right) {
      closestX = rect.right;
    } else {
      closestX = clickX;
    }

    // Determine if the click is closer to the top or bottom of the canvas.
    if (clickY < rect.top) {
      closestY = rect.top;
    } else if (clickY > rect.bottom) {
      closestY = rect.bottom;
    } else {
      closestY = clickY;
    }

    return { x: closestX, y: closestY };
  }

  $canvasPanel.on("wheel", function (event) {
    event.preventDefault();

    var closestPoint = calculateClosestBorderPoint(
      canvas.getBoundingClientRect(),
      event.originalEvent.pageX,
      event.originalEvent.pageY
    );

    var borderX = closestPoint.x - $canvasPanel.offset().left;
    var borderY = closestPoint.y - $canvasPanel.offset().top;

    var zoomDirection = event.originalEvent.deltaY < 0 ? 1 : -1;

    if (scale == MIN_SCALE && zoomDirection == -1) return;
    if (scale == MAX_SCALE && zoomDirection == 1) return;
    
    var nextScale = scale * zoomFactor ** zoomDirection;

    nextScale = Math.min(nextScale, MAX_SCALE);
    nextScale = Math.max(nextScale, MIN_SCALE);

    var percentXInCurrentBox = borderX / canvasPanelWidth;
    var percentYInCurrentBox = borderY / canvasPanelHeight;

    var currentBoxWidth = canvasPanelWidth / scale;
    var currentBoxHeight = canvasPanelHeight / scale;

    var nextBoxWidth = canvasPanelWidth / nextScale;
    var nextBoxHeight = canvasPanelHeight / nextScale;

    var deltaX =
      (nextBoxWidth - currentBoxWidth) * (percentXInCurrentBox - 0.5);
    var deltaY =
      (nextBoxHeight - currentBoxHeight) * (percentYInCurrentBox - 0.5);

    var nextOffsetX = offsetX - deltaX;
    var nextOffsetY = offsetY - deltaY;

    $canvas.css({
      transform: "scale(" + nextScale + ")",
      left: -1 * nextOffsetX * nextScale,
      right: nextOffsetX * nextScale,
      top: -1 * nextOffsetY * nextScale,
      bottom: nextOffsetY * nextScale,
    });

    offsetX = nextOffsetX;
    offsetY = nextOffsetY;
    scale = nextScale;
  });
</script>

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