在下面的代码中我遇到了缩放问题。当鼠标位于一个位置并且我缩放到该位置时,一切都很好。但是,当我将鼠标移动到另一个位置并在那里缩放时,画布就会移动到另一个位置。我认为问题在于缩放后,新的鼠标位置由于缩放而不正确。我怎样才能正确地重新计算它?
缩放系数必须始终为 0.05 -> 1.0、0.95、0.9 并返回 0.9、0.95、1.0
变焦范围从0.3到1.0
const container = document.getElementById('container');
const citycanvas = document.getElementById('citycanvas');
const citycontext = citycanvas.getContext('2d');
// Funktion zum Zeichnen von Bergen
function drawMountains(context) {
const gradient = context.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, "#000000");
gradient.addColorStop(1, "#000000");
context.fillStyle = gradient;
context.beginPath();
let startX = 200;
let startY = 400;
let width = 300;
let height = 200;
context.moveTo(startX, startY);
context.lineTo(startX + width / 2, startY - height);
context.lineTo(startX + width, startY);
context.lineTo(startX, startY);
let mountainData = [
{ startX: 500, startY: 400, width: 250, height: 150 },
{ startX: 800, startY: 400, width: 350, height: 250 },
{ startX: 1100, startY: 400, width: 200, height: 100 }
];
mountainData.forEach(mountain => {
context.moveTo(mountain.startX, mountain.startY);
context.lineTo(mountain.startX + mountain.width / 2, mountain.startY - mountain.height);
context.lineTo(mountain.startX + mountain.width, mountain.startY);
context.lineTo(mountain.startX, mountain.startY);
});
context.closePath();
context.fill();
}
drawMountains(citycontext);
let Zoomfaktor = 0.05;
const min_Faktor = 0.3;
const max_Faktor = 1.0;
let mouseX, mouseY;
let Zoomlevel = 1.0;
let woldX = 0;
let woldY = 0;
let windowcursorposX = 0;
let windowcursorposY = 0;
let mapleftposX = 0;
let mapleftposY = 0;
let mapcursorposX = 0;
let mapcursorposY = 0;
// Bestimmt die Cursorposition auf der Map
function cursorpos(e)
{
woldX = windowcursorposX;
woldY = windowcursorposY;
console.log('woldX: ' + woldX);
// console.log('woldY: ' + woldY);
windowcursorposX = parseInt(e.clientX);
windowcursorposY = parseInt(e.clientY);
console.log('windowcursorposX: ' + windowcursorposX);
// console.log('windowcursorposY: ' + windowcursorposY);
// Berechne die tatsächliche Position auf der Karte basierend auf dem aktuellen Zoomlevel
mapcursorposX = windowcursorposX - mapleftposX;
mapcursorposY = windowcursorposY - mapleftposY;
console.log('mapleftposX: ' + mapleftposX);
//console.log('mapleftposY: ' + mapleftposY);
console.log('mapcursorposX: ' + mapcursorposX);
//console.log('mapcursorposY: ' + mapcursorposY);
}
// Aktualisiere die Mausposition bei jeder Bewegung
citycanvas.addEventListener('mousemove', (e) => {
cursorpos(e);
});
// Zoomfunktion nicht löschen und verändern
citycanvas.addEventListener('wheel', (e) =>
{
e.preventDefault();
cursorpos(e);
// Richtung des Mausrads bestimmen
const delta = e.deltaY > 0 ? -1 : 1; // Zoom-In (delta < 0) und Zoom-Out (delta > 0)
console.log('delta: ' + delta);
// Berechnung des neuen Zoomlevels
let newZoomLevel = Zoomlevel - (delta * Zoomfaktor);
// Begrenzung des Zoomlevels (optional)
newZoomLevel = Math.max(min_Faktor, Math.min(max_Faktor, newZoomLevel));
console.log('newZoomLevel: ' + newZoomLevel);
// Nur weiterfahren, wenn das neue Zoomlevel sich ändert
if (newZoomLevel !== Zoomlevel)
{
// Berechnung des neuen Transformationsursprungs
const newPosX = mapcursorposX / Zoomlevel;
const newPosY = mapcursorposY / Zoomlevel;
console.log('mapcursorposX: ' + mapcursorposX);
console.log('mapcursorposY: ' + mapcursorposY);
console.log('Zoomlevel: ' + Zoomlevel);
console.log('newPosX: ' + newPosX);
console.log('newPosY: ' + newPosY);
// Setze den neuen Zoomfaktor und den neuen Transformationsursprung
citycanvas.style.transformOrigin = `${newPosX}px ${newPosY}px`;
citycanvas.style.transform = `scale(${newZoomLevel})`;
// Position der Maus relativ zum Canvas
const rect = citycanvas.getBoundingClientRect();
mapleftposX = rect.left;
mapleftposY = rect.top;
console.log('mapleftposX: ' + mapleftposX);
console.log('mapleftposY: ' + mapleftposY);
// Aktualisiere das Zoomlevel
Zoomlevel = newZoomLevel;
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas Zoom</title>
<style>
html, body
{
overflow: hidden;
}
#container {
width: 100vw; /* Nimmt die gesamte Breite des Fensters ein */
height: 100vh; /* Nimmt die gesamte Höhe des Fensters ein */
overflow: hidden;
position: relative;
background-color: lightgrey; /* Hilfreich zum Debuggen */
}
#citycanvas {
position: absolute;
transform-origin: top left;
background-color: green;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container">
<canvas id="citycanvas" width="12600" height="9600"></canvas>
</div>
<script src="zoom.js"></script>
</body>
</html>
首先也是最重要的,我想说在每个 mousemove 事件上查询鼠标位置是多余的,因为一旦轮子事件被触发,它就已经被查询过 - 并且只是需要的。
所以摆脱:
citycanvas.addEventListener('mousemove', (e) => {
cursorpos(e);
});
其次,当您实际缩放时,您始终使用鼠标在屏幕上的实际位置。因此,当然,一旦用户将其移动到其他位置,您也会移动缩放的中心点。
为了解决这个问题,我建议设置一个全局变量,如果缩放级别等于 1,它将跟踪鼠标位置,并使用该点进行缩放计算。
const container = document.getElementById('container');
const citycanvas = document.getElementById('citycanvas');
const citycontext = citycanvas.getContext('2d');
// Funktion zum Zeichnen von Bergen
function drawMountains(context) {
const gradient = context.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, "#000000");
gradient.addColorStop(1, "#000000");
context.fillStyle = gradient;
context.beginPath();
let startX = 200;
let startY = 400;
let width = 300;
let height = 200;
context.moveTo(startX, startY);
context.lineTo(startX + width / 2, startY - height);
context.lineTo(startX + width, startY);
context.lineTo(startX, startY);
let mountainData = [{
startX: 500,
startY: 400,
width: 250,
height: 150
},
{
startX: 800,
startY: 400,
width: 350,
height: 250
},
{
startX: 1100,
startY: 400,
width: 200,
height: 100
}
];
mountainData.forEach(mountain => {
context.moveTo(mountain.startX, mountain.startY);
context.lineTo(mountain.startX + mountain.width / 2, mountain.startY - mountain.height);
context.lineTo(mountain.startX + mountain.width, mountain.startY);
context.lineTo(mountain.startX, mountain.startY);
});
context.closePath();
context.fill();
}
drawMountains(citycontext);
let Zoomfaktor = 0.05;
const min_Faktor = 0.3;
const max_Faktor = 1.0;
let mouseX, mouseY;
let Zoomlevel = 1.0;
let woldX = 0;
let woldY = 0;
let windowcursorposX = 0;
let windowcursorposY = 0;
let mapleftposX = 0;
let mapleftposY = 0;
let mapcursorposX = 0;
let mapcursorposY = 0;
// Bestimmt die Cursorposition auf der Map
function cursorpos(e) {
windowcursorposX = woldX;
windowcursorposY = woldX;
console.log('windowcursorposX: ' + windowcursorposX);
// console.log('windowcursorposY: ' + windowcursorposY);
// Berechne die tatsächliche Position auf der Karte basierend auf dem aktuellen Zoomlevel
mapcursorposX = windowcursorposX - mapleftposX;
mapcursorposY = windowcursorposY - mapleftposY;
console.log('mapleftposX: ' + mapleftposX);
//console.log('mapleftposY: ' + mapleftposY);
console.log('mapcursorposX: ' + mapcursorposX);
//console.log('mapcursorposY: ' + mapcursorposY);
}
// Zoomfunktion nicht löschen und verändern
citycanvas.addEventListener('wheel', (e) => {
e.preventDefault();
// Richtung des Mausrads bestimmen
const delta = e.deltaY > 0 ? -1 : 1; // Zoom-In (delta < 0) und Zoom-Out (delta > 0)
console.log('delta: ' + delta);
if (Zoomlevel == 1) {
woldX = parseInt(e.clientX);
woldY = parseInt(e.clientY);
}
// Berechnung des neuen Zoomlevels
let newZoomLevel = Zoomlevel - (delta * Zoomfaktor);
// Begrenzung des Zoomlevels (optional)
newZoomLevel = Math.max(min_Faktor, Math.min(max_Faktor, newZoomLevel));
console.log('newZoomLevel: ' + newZoomLevel);
cursorpos();
// Nur weiterfahren, wenn das neue Zoomlevel sich ändert
if (newZoomLevel !== Zoomlevel) {
// Berechnung des neuen Transformationsursprungs
const newPosX = mapcursorposX / Zoomlevel;
const newPosY = mapcursorposY / Zoomlevel;
console.log('mapcursorposX: ' + mapcursorposX);
console.log('mapcursorposY: ' + mapcursorposY);
console.log('Zoomlevel: ' + Zoomlevel);
console.log('newPosX: ' + newPosX);
console.log('newPosY: ' + newPosY);
// Setze den neuen Zoomfaktor und den neuen Transformationsursprung
citycanvas.style.transformOrigin = `${newPosX}px ${newPosY}px`;
citycanvas.style.transform = `scale(${newZoomLevel})`;
// Position der Maus relativ zum Canvas
const rect = citycanvas.getBoundingClientRect();
mapleftposX = rect.left;
mapleftposY = rect.top;
console.log('mapleftposX: ' + mapleftposX);
console.log('mapleftposY: ' + mapleftposY);
// Aktualisiere das Zoomlevel
Zoomlevel = newZoomLevel;
}
});
html,
body {
overflow: hidden;
}
#container {
width: 100vw;
/* Nimmt die gesamte Breite des Fensters ein */
height: 100vh;
/* Nimmt die gesamte Höhe des Fensters ein */
overflow: hidden;
position: relative;
background-color: lightgrey;
/* Hilfreich zum Debuggen */
}
#citycanvas {
position: absolute;
transform-origin: top left;
background-color: green;
overflow: hidden;
}
<div id="container">
<canvas id="citycanvas" width="12600" height="9600"></canvas>
</div>