像 Aseprite 这样应用缩放到点

问题描述 投票:0回答:1

您好,我需要一些帮助来将另一个问题的答案应用到我的代码中。我目前正在尝试重新创建 aseprite 的缩放功能,但是使用我当前的代码,当我缩放画布的不同侧面时,它会移动几个像素。

之前我被告知我的问题是以下内容的重复:放大一个点(使用缩放和平移)

我看过:https://stackoverflow.com/a/3151987/24209432,但我似乎无法得到他们使用我的代码的答案。请帮我解决这个问题。

这里有一个 gif 显示当前发生的情况:缩放移位问题

它应该如何工作: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>
javascript html css zooming scale
1个回答
0
投票

这有效,如果需要,请随时提问:)

<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: 600px;
    height: 600px;
    background-color: white;
    position: relative;
    overflow: hidden;
  }
  #canvas {
    position: absolute;
    margin: 0;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    background-color: red;
    width: 100px;
    height: 100px;
  }
</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 $image = $("#canvas");
  var $container = $("#canvas-panel");

  var areaWidth = $container.width();
  var areaHeight = $container.height();

  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 };
  }

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

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

    var clientX = closestPoint.x - $container.offset().left;
    var clientY = closestPoint.y - $container.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);

    console.log(nextScale);

    var percentXInCurrentBox = clientX / areaWidth;
    var percentYInCurrentBox = clientY / areaHeight;

    var currentBoxWidth = areaWidth / scale;
    var currentBoxHeight = areaHeight / scale;

    var nextBoxWidth = areaWidth / nextScale;
    var nextBoxHeight = areaHeight / nextScale;

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

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

    $image.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>

© www.soinside.com 2019 - 2024. All rights reserved.