繁体   English   中英

Javascript伪缩放画布中的鼠标位置

[英]Javascript pseudo-zoom around mouse position in canvas

我想围绕画布中的鼠标位置“缩放”(鼠标滚轮)我拥有的网格(就像 Desmos 一样)。 通过缩放,我的意思是重新绘制画布内的线条以看起来像缩放效果,而不是执行实际的缩放。 而且我只想使用 vanilla javascript 而没有库(这样我就可以学习)。

此时,我设置了一个非常基本的放大效果,它仅根据鼠标滚轮值乘以网格线之间的距离。

 //////////////////////////////////////////////////////////////////////////////// // User contants const canvasWidth = 400; const canvasHeight = 200; const canvasBackground = '#282c34'; const gridCellColor = "#777"; const gridBlockColor = "#505050"; const axisColor = "white"; // Internal constants const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d', { alpha: false }); const bodyToCanvas = 8; //////////////////////////////////////////////////////////////////////////////// // User variables let cellSize = 10; let cellBlock = 5; let xSubdivs = 40 let ySubdivs = 20 // Internal variables let grid = ''; let zoom = 0; let xAxisOffset = xSubdivs/2; let yAxisOffset = ySubdivs/2; let mousePosX = 0; let mousePosY = 0; //////////////////////////////////////////////////////////////////////////////// // Classes class Grid{ constructor() { this.width = canvasWidth, this.height = canvasHeight, this.cellSize = cellSize, this.cellBlock = cellBlock, this.xSubdivs = xSubdivs, this.ySubdivs = ySubdivs } draw(){ // Show canvas context.fillStyle = canvasBackground; context.fillRect(-this.width/2, -this.height/2, this.width, this.height); // Horizontal lines this.xSubdivs = Math.floor(this.height / this.cellSize); for (let i = 0; i <= this.xSubdivs; i++) {this.setHorizontalLines(i);} // Vertical lines this.ySubdivs = Math.floor(this.width / this.cellSize); for (let i = 0; i <= this.ySubdivs; i++) {this.setVerticalLines(i) ;} // Axis this.setAxis(); } setHorizontalLines(i) { // Style context.lineWidth = 0.5; if (i % this.cellBlock == 0) { // light lines context.strokeStyle = gridCellColor; } else{ // Dark lines context.strokeStyle = gridBlockColor; } //Draw lines context.beginPath(); context.moveTo(-this.width/2, (this.cellSize * i) - this.height/2); context.lineTo( this.width/2, (this.cellSize * i) - this.height/2); context.stroke(); context.closePath(); } setVerticalLines(i) { // Style context.lineWidth = 0.5; if (i % cellBlock == 0) { // Light lines context.strokeStyle = gridCellColor; } else { // Dark lines context.strokeStyle = gridBlockColor; } //Draw lines context.beginPath(); context.moveTo((this.cellSize * i) - this.width/2, -this.height/2); context.lineTo((this.cellSize * i) - this.width/2, this.height/2); context.stroke(); context.closePath(); } // Axis are separated from the line loops so that they remain on // top of them (cosmetic measure) setAxis(){ // Style x Axis context.lineWidth = 1.5; context.strokeStyle = axisColor; // Draw x Axis context.beginPath(); context.moveTo(-this.width/2, (this.cellSize * yAxisOffset) - this.height/2); context.lineTo( this.width/2, (this.cellSize * yAxisOffset) - this.height/2); context.stroke(); context.closePath(); // Style y axis context.lineWidth = 1.5; context.strokeStyle = axisColor; // Draw y axis context.beginPath(); context.moveTo((this.cellSize * xAxisOffset ) - this.width/2, -this.height/2); context.lineTo((this.cellSize * xAxisOffset ) - this.width/2, this.height/2); context.stroke(); context.closePath(); } } //////////////////////////////////////////////////////////////////////////////// // Functions function init() { // Set up canvas if (window.devicePixelRatio > 1) { canvas.width = canvasWidth * window.devicePixelRatio; canvas.height = canvasHeight * window.devicePixelRatio; context.scale(window.devicePixelRatio, window.devicePixelRatio); } else { canvas.width = canvasWidth; canvas.height = canvasHeight; } canvas.style.width = canvasWidth + "px"; canvas.style.height = canvasHeight + "px"; // Initialize coordinates in the middle of the canvas context.translate(canvasWidth/2,canvasHeight/2) // Setup the grid grid = new Grid(); // Display the grid grid.draw(); } function setZoom(){ grid.cellSize = grid.cellSize + zoom; grid.draw(); } //////////////////////////////////////////////////////////////////////////////// //Launch the page init(); //////////////////////////////////////////////////////////////////////////////// // Update the page on resize window.addEventListener("resize", init); // Zoom the canvas with mouse wheel canvas.addEventListener('mousewheel', function (e) { e.preventDefault(); e.stopPropagation(); zoom = e.wheelDelta/120; requestAnimationFrame(setZoom); }) // Get mouse coordinates on mouse move. canvas.addEventListener('mousemove', function (e) { e.preventDefault(); e.stopPropagation(); mousePosX = parseInt(e.clientX)-bodyToCanvas ; mousePosY = parseInt(e.clientY)-bodyToCanvas ; }) ////////////////////////////////////////////////////////////////////////////////
 html, body { background:#21252b; width:100%; height:100%; margin:0px; padding:0px; overflow: hidden; } span{ color:white; font-family: arial; font-size: 12px; margin:8px; } #canvas{ margin:8px; border: 1px solid white; }
 <span>Use mouse wheel to zoom</span> <canvas id="canvas"></canvas>

这工作正常,但正如预期的那样,它从左上角而不是从鼠标位置放大网格。

因此,我考虑检测鼠标位置,然后修改所有“moveTo”和“lineTo”部分。 目标是偏移放大的网格,以便除了与当前鼠标坐标相交的 2 条线之外的所有内容都被移位。

例如,在我看来,而不是这样:

context.moveTo(
    (this.cellSize * i) - this.width/2, 
    -this.height/2
);

我应该有这样的东西:

context.moveTo(
    (this.cellSize * i) - this.width/2 + OFFSET BASED ON MOUSE COORDS, 
    -this.height/2
);

但是经过数小时的试验和错误,我被困住了。 因此,任何帮助将不胜感激。

仅供参考:我已经编写了一个功能性的平移系统,花了我几天的时间来实现(为了清楚起见,我从这里的代码中删除了),所以如果可能的话,我想保留我到目前为止的大部分逻辑。 当然,除非我的代码完全是胡说八道或存在性能问题。

谢谢你。

您已经在以像素为单位跟踪鼠标位置。 如果将鼠标位置转换为网格的坐标系,则可以定义它悬停的网格单元格。

放大后,您可以再次计算鼠标的坐标。 围绕另一个中心点放大时,您会看到坐标偏移。

您可以通过将网格的中心向相反方向平移来撤消该移动。

这是它的主要部分:

function setZoom() {
    // Calculate the mouse position before applying the zoom
    // in the coordinate system of the grid
    const x1 = (mousePosX - grid.centerX) / grid.cellSize;
    const y1 = (mousePosY - grid.centerY) / grid.cellSize;
        
    // Make the zoom happen: update the cellSize
    grid.cellSize = grid.cellSize + zoom;
        
    // After zoom, you'll see the coordinates changed
    const x2 = (mousePosX - grid.centerX) / grid.cellSize;
    const y2 = (mousePosY - grid.centerY) / grid.cellSize;
        
    // Calculate the shift
    const dx = x2 - x1;
    const dy = y2 - y1;
        
    // "Undo" the shift by shifting the coordinate system's center
    grid.centerX += dx * grid.cellSize;
    grid.centerY += dy * grid.cellSize;
        
    grid.draw();
}

为了使这更容易使用,我引入了grid.centerX.centerY 我更新了你的draw方法以考虑中心(并且一路杀了它,但我认为你会设法改进一点)。

这是完整的示例:

 //////////////////////////////////////////////////////////////////////////////// // User contants const canvasWidth = 400; const canvasHeight = 200; const canvasBackground = '#282c34'; const gridCellColor = "#777"; const gridBlockColor = "#505050"; const axisColor = "white"; // Internal constants const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d', { alpha: false }); const bodyToCanvas = 8; //////////////////////////////////////////////////////////////////////////////// // User variables let cellSize = 10; let cellBlock = 5; let xSubdivs = 40 let ySubdivs = 20 // Internal variables let grid = ''; let zoom = 0; let xAxisOffset = xSubdivs/2; let yAxisOffset = ySubdivs/2; let mousePosX = 0; let mousePosY = 0; //////////////////////////////////////////////////////////////////////////////// // Classes class Grid{ constructor() { this.width = canvasWidth, this.height = canvasHeight, this.cellSize = cellSize, this.cellBlock = cellBlock, this.xSubdivs = xSubdivs, this.ySubdivs = ySubdivs, this.centerX = canvasWidth / 2, this.centerY = canvasHeight / 2 } draw(){ // Show canvas context.fillStyle = canvasBackground; context.fillRect(0, 0, this.width, this.height); // Horizontal lines const minIY = -Math.ceil(this.centerY / this.cellSize); const maxIY = Math.ceil((this.height - this.centerY) / this.cellSize); for (let i = minIY; i <= maxIY; i++) {this.setHorizontalLines(i);} // Vertical lines const minIX = -Math.ceil(this.centerX / this.cellSize); const maxIX = Math.ceil((this.width - this.centerX) / this.cellSize); for (let i = minIX; i <= maxIX; i++) {this.setVerticalLines(i) ;} this.setVerticalLines(0); this.setHorizontalLines(0); } setLineStyle(i) { if (i === 0) { context.lineWidth = 1.5; context.strokeStyle = axisColor; } else if (i % cellBlock == 0) { // Light lines context.lineWidth = 0.5; context.strokeStyle = gridCellColor; } else { // Dark lines context.lineWidth = 0.5; context.strokeStyle = gridBlockColor; } } setHorizontalLines(i) { // Style this.setLineStyle(i); //Draw lines const y = this.centerY + this.cellSize * i; context.beginPath(); context.moveTo(0, y); context.lineTo(this.width, y); context.stroke(); context.closePath(); } setVerticalLines(i) { // Style this.setLineStyle(i); //Draw lines const x = this.centerX + this.cellSize * i; context.beginPath(); context.moveTo(x, 0); context.lineTo(x, this.height); context.stroke(); context.closePath(); } } //////////////////////////////////////////////////////////////////////////////// // Functions function init() { // Set up canvas if (window.devicePixelRatio > 1) { canvas.width = canvasWidth * window.devicePixelRatio; canvas.height = canvasHeight * window.devicePixelRatio; context.scale(window.devicePixelRatio, window.devicePixelRatio); } else { canvas.width = canvasWidth; canvas.height = canvasHeight; } canvas.style.width = canvasWidth + "px"; canvas.style.height = canvasHeight + "px"; // Setup the grid grid = new Grid(); // Display the grid grid.draw(); } function setZoom() { // Calculate the mouse position before applying the zoom // in the coordinate system of the grid const x1 = (mousePosX - grid.centerX) / grid.cellSize; const y1 = (mousePosY - grid.centerY) / grid.cellSize; grid.cellSize = grid.cellSize + zoom; // After zoom, you'll see the coordinates changed const x2 = (mousePosX - grid.centerX) / grid.cellSize; const y2 = (mousePosY - grid.centerY) / grid.cellSize; // Calculate the shift const dx = x2 - x1; const dy = y2 - y1; // "Undo" the shift by shifting the coordinate system's center grid.centerX += dx * grid.cellSize; grid.centerY += dy * grid.cellSize; grid.draw(); } //////////////////////////////////////////////////////////////////////////////// //Launch the page init(); //////////////////////////////////////////////////////////////////////////////// // Update the page on resize window.addEventListener("resize", init); // Zoom the canvas with mouse wheel canvas.addEventListener('mousewheel', function (e) { e.preventDefault(); e.stopPropagation(); zoom = e.wheelDelta/120; requestAnimationFrame(setZoom); }) // Get mouse coordinates on mouse move. canvas.addEventListener('mousemove', function (e) { e.preventDefault(); e.stopPropagation(); mousePosX = parseInt(e.clientX)-bodyToCanvas ; mousePosY = parseInt(e.clientY)-bodyToCanvas ; }) ////////////////////////////////////////////////////////////////////////////////
 html, body { background:#21252b; width:100%; height:100%; margin:0px; padding:0px; overflow: hidden; } span{ color:white; font-family: arial; font-size: 12px; margin:8px; } #canvas{ margin:8px; border: 1px solid white; }
 <canvas id="canvas"></canvas>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM