调整画布大小和更新滚动条位置时,缩放画布不起作用

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

我正在尝试在画布元素上实现缩放功能,其中:

  • 缩放应以鼠标光标为中心。
  • 画布应正确缩放。
  • 滚动条应更新以反映新的放大尺寸。

但是,当我尝试在缩放后更新画布大小和滚动条位置时,缩放不再按预期运行。内容发生变化,滚动条无法正确更新。

let canvas;
let ctx;
let originalCanvasHeight, originalCanvasWidth;

setTimeout(() => {
  canvas = document.getElementById("canvas")
  ctx = canvas.getContext("2d")
  originalCanvasWidth = canvas.getBoundingClientRect().width;
  originalCanvasHeight = canvas.getBoundingClientRect().height;
  console.log(originalCanvasHeight)
  ctx.fillStyle = "red"
  ctx.fillRect(50, 50, 40, 40)

}, 2000)

let scale = 1;
const scaleOffset = {
  x: 0,
  y: 0
}

const zoom = (e) => {

  const mousex = 70
  const mousey = 70

  const zoomStep = 0.5
  const targetScale = scale + (e.deltaY > 0 ? -zoomStep : zoomStep)
  console.log(targetScale)
  const scaleChangeFactor = parseFloat((targetScale / scale).toFixed(2))


  // Adjust offsets so that the zoom centers on the mouse position
  scaleOffset.x = parseFloat(
    (mousex - (mousex - scaleOffset.x) * scaleChangeFactor).toFixed(2),
  )
  scaleOffset.y = parseFloat(
    (mousey - (mousey - scaleOffset.y) * scaleChangeFactor).toFixed(2),
  )

  canvas.width = originalCanvasWidth * targetScale
  canvas.height = originalCanvasHeight * targetScale

  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.setTransform(1, 0, 0, 1, 0, 0)
  ctx.translate(scaleOffset.x, scaleOffset.y)
  ctx.scale(targetScale, targetScale)
  ctx.fillStyle = "red";
  ctx.fillRect(50, 50, 40, 40);

  scale = targetScale;
}
.canvas-container {
  box-sizing: border-box;
  height: 80vh;
  overflow: auto;
  width: 100%;
  padding: 0;
  margin: 0;
  border: 2px solid red;
  position: relative;
}

.canvas {
  min-height: calc(100% - 10px);
  /* to avoid rounding issue related to fractional pixels calculation */
  min-width: 100%;
  /* to avoid rounding issue related to fractional pixels calculation */
  background-image: linear-gradient( rgba(0, 0, 0, 0) 10px, rgba(0, 0, 0, .08) 11px), linear-gradient(90deg, rgba(0, 0, 0, 0) 10px, rgba(0, 0, 0, .08) 11px);
  background-size: 11px 11px, 11px 11px;
  box-sizing: border-box;
  border: 3px solid green;
  height: 100%;
}
<button onClick="zoom(event)">Zoom</button>
<div ref="canvasContainer" class="canvas-container">
  <canvas id="canvas" class="canvas"></canvas>
</div>

问题:

  1. 缩放不居中:调整画布大小或更新滚动条后,缩放不会保持在鼠标位置居中。

  2. 滚动条未更新:我尝试使用scrollLeft和scrollTop值更新滚动位置,但它们似乎无法正确调整缩放。

  3. 画布调整大小会破坏缩放:当我动态调整画布大小时,缩放行为会被破坏,并且我会失去鼠标周围的正确焦点。

预期行为:

  1. 缩放行为:缩放应保持在鼠标位置的中心。

  2. 画布和滚动条更新:画布应变形,滚动条应根据缩放的内容移动。

  3. 无需调整大小:我不想直接调整画布大小。我希望变换能够处理缩放。

因此,我正在全球范围内尝试重现figma工坊的行为。

我尝试了很多方法但没有效果,我想知道我错过了什么

为什么当我调整画布大小并更新滚动条时缩放行为停止工作?如何保持缩放以鼠标为中心并正确更新滚动条而不导致意外行为?

NB:我模拟鼠标位置,位于矩形的中心,因为我通过单击按钮触发缩放。

请注意,当我注释掉画布大小的调整和滚动条的替换时,缩放可以工作,但这不是预期的行为

javascript canvas html5-canvas zooming
1个回答
0
投票

关键是使用

ctx.translate()
ctx.scale()
将变换应用于画布,而无需调整实际画布的大小。然后手动调整滚动位置以保持缩放以鼠标位置为中心。

let canvas, ctx;
let originalCanvasWidth, originalCanvasHeight;

setTimeout(() => {
  canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");
  originalCanvasWidth = canvas.getBoundingClientRect().width;
  originalCanvasHeight = canvas.getBoundingClientRect().height;
  ctx.fillStyle = "red";
  ctx.fillRect(50, 50, 40, 40);
}, 2000);

let scale = 1;
let offsetX = 0;
let offsetY = 0;

const zoom = (e) => {
  const canvasContainer = document.querySelector(".canvas-container");
  const rect = canvas.getBoundingClientRect();

  // Simulated mouse position at the center of the rectangle
  const mousex = 70; // Simulated X coordinate
  const mousey = 70; // Simulated Y coordinate

  // Calculate the position relative to the canvas's top left corner
  const mouseXRelative = mousex - rect.left + canvasContainer.scrollLeft;
  const mouseYRelative = mousey - rect.top + canvasContainer.scrollTop;

  // Zoom in or out
  const zoomStep = 0.1;
  const direction = e.deltaY > 0 ? -zoomStep : zoomStep;
  const newScale = scale + direction;
  if (newScale < 0.1) return; // Prevent negative or too small scale

  // Calculate scaling factor
  const scaleFactor = newScale / scale;

  // Adjust offsets to center zoom on the mouse position
  offsetX = mouseXRelative - (mouseXRelative - offsetX) * scaleFactor;
  offsetY = mouseYRelative - (mouseYRelative - offsetY) * scaleFactor;

  // Apply transformations
  ctx.setTransform(1, 0, 0, 1, 0, 0); // Reset transformation matrix
  ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
  ctx.translate(offsetX, offsetY);
  ctx.scale(newScale, newScale);
  ctx.fillStyle = "red";
  ctx.fillRect(50, 50, 40, 40);

  // Adjust the container's scroll position to reflect zoom changes
  canvasContainer.scrollLeft = offsetX * scaleFactor;
  canvasContainer.scrollTop = offsetY * scaleFactor;

  // Update scale
  scale = newScale;
};

// Event listener for zoom (e.g., wheel event)
document.querySelector("button").addEventListener("click", zoom);

确保

canvas
具有正确的 css 属性来显示滚动条,并且
scrollLeft
scrollTop
根据转换后的内容进行更新。

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