簡體   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