Javascript画布:在给定坐标处应用缩放

col*_*lxi 8 javascript html5 canvas zoom scale

我正在努力实现使用Vanilla Javascript的画布缩放(在鼠标滚轮上),但没有成功.

当滚轮转动时,必须在鼠标坐标上应用缩放.之前有人问过,但是我的情况完全不同,考虑到我不能使用canvasContext.translate,offsetX并且offsetY必须保持其绝对表示的值(不是缩放的)

我真的很感激一些亮点.

在下面的代码片段中,我提供了当前和功能失调的实现.当您放大和缩小保持相同的缩放坐标时,您将看到它是如何工作的,但只要您将鼠标移动到新坐标(当缩放!= 1时)并继续缩放时,新的偏移就会出错.

// initiate variabks : canvas ref, offsets, scale...
const context   = document.getElementById('c').getContext('2d');
let scale       = 1;
let scaleFactor = 0.2;
let offsetX=0;
let offsetY=0;

// Handle mousenwheel zoom
context.canvas.onwheel= function(e){
  e.preventDefault();
  // calculate scale direction 6 new value
  let direction = e.deltaY > 0 ? 1 : -1;
  scale += scaleFactor * direction;
  // calculatethe new offsets (unscaled values)
  offsetX = e.offsetX - (e.offsetX  / scale);
  offsetY = e.offsetY - (e.offsetY / scale);  
  // apply new scale
  context.setTransform(1, 0, 0, 1, 0, 0);
  context.scale(scale, scale);
}

// clear canvas and draw two boxes
// NO CHANGES CAN BE DONE IN THIS FUNCTION
function render(){
  context.beginPath();
  context.clearRect(0,0,context.canvas.width/scale,context.canvas.height/scale);
  context.rect(100-offsetX,50-offsetY,50,50);
  context.rect(200-offsetX,50-offsetY,50,50);
  context.stroke();
  
  document.getElementById("info").innerHTML=`
    Scale : ${scale} <br>
    Offets : ${ Math.round(offsetX) + ' , ' + Math.round(offsetY)} <br>
  `;
  requestAnimationFrame( render );
}
render()
Run Code Online (Sandbox Code Playgroud)
<canvas id="c" width="350" height="150" style="border: 1px solid red;"></canvas>
<div id="info"></div>
Run Code Online (Sandbox Code Playgroud)

col*_*lxi 7

我终于得到了... 画布任意坐标缩放而不使用context.translate()

我附上我的解决方案,有一点奖励:画布平移(光标键滚动).我希望对某人有用.

// initiate variables : canvas ref, offsets, scale...
const context   = document.getElementById('c').getContext('2d');
let scale       = 1;
let scaleFactor = 0.2;
let scrollX     = 0;
let scrollY     = 0;

let info        = document.getElementById("info");


// Handle mousenwheel zoom
context.canvas.onwheel =  function(e){
  e.preventDefault();
  let previousScale= scale;
  
  // calculate scale direction 6 new scale value
  let direction = e.deltaY > 0 ? 1 : -1;
  scale += scaleFactor * direction;

  // calculate the new scroll values
  scrollX += ( e.offsetX / previousScale )  - (e.offsetX  / scale);
  scrollY += ( e.offsetY / previousScale ) - ( e.offsetY / scale);
  
  // apply new scale in a non acumulative way
  context.setTransform(1, 0, 0, 1, 0, 0);
  context.scale(scale, scale);
}


// clear canvas and draw two boxes
function render(){
  context.beginPath();
  context.clearRect(0,0,context.canvas.width/scale, context.canvas.height/scale);
  context.rect(100-scrollX,50-scrollY,50,50);
  context.rect(200-scrollX,50-scrollY,50,50);
  context.stroke();
  
  info.innerHTML=`
    Scale : ${scale} <br>
    Scroll: ${scrollX},${scrollY} <br>
  `
  requestAnimationFrame( render );
}

// handlencursor keys to move scroll
window.onkeydown = function(event){
    event.preventDefault();
    if(event.keyCode == 37)      scrollX -=10;
    else if(event.keyCode == 39) scrollX +=10;
    else if(event.keyCode == 38) scrollY -=10;
    else if(event.keyCode == 40) scrollY +=10;
};

context.canvas.focus()
render()
Run Code Online (Sandbox Code Playgroud)
<canvas id="c" width="400" height="150" style="border: 1px solid red;" tabindex="1"></canvas>
<div id="info"></div>
Run Code Online (Sandbox Code Playgroud)