您好,我需要一些帮助来将另一个问题的答案应用到我的代码中。我目前正在尝试重新创建 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>
这有效,如果需要,请随时提问:)
<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>