Javascript Canvas: применить масштабирование по заданным координатам - PullRequest
0 голосов
/ 22 октября 2018

Я борюсь с реализацией масштабирования холста (на колесе мыши), используя 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()
<canvas id="c" width="350" height="150" style="border: 1px solid red;"></canvas>
<div id="info"></div>

Ответы [ 2 ]

0 голосов
/ 24 октября 2018

Я наконец-то понял ... масштабирование произвольных координат холста без использования 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()
<canvas id="c" width="400" height="150" style="border: 1px solid red;" tabindex="1"></canvas>
<div id="info"></div>
0 голосов
/ 22 октября 2018

Основная идея такова:

  context.translate(cx, cy);
  context.scale(scale, scale);
  context.translate(-cx, -cy);

, где cx и cy - координаты центра группы объектов.

Наблюдение: зеркальные объекты с отрицательной шкалой.Вот почему я использовал заливку красным и черным.

// global vars : canvas, scroll & scale
let info        = document.getElementById("info");
const canvas    = document.getElementById('c');
let cw = canvas.width = 350,cx = cw/2;
let ch = canvas.height = 150,cy = 75;
const context   = c.getContext('2d');
let scrollX     = 0;
let scrollY     = 0;
let scale       = 1;
let scaleFactor = 0.02;


// clear canvas and draw two boxes
function render(){
  //context.clearRect(0, 0, canvas.width/scale, canvas.height/scale);
 
  
  context.beginPath();
  context.rect(100,50,50,50);
  context.fillStyle = "red";
  context.fill();
  
  context.beginPath();
  context.rect(200,50,50,50);
  context.fillStyle = "black";
  context.fill();
  
  info.innerHTML=`Scroll: ${scrollX},${scrollY} - Scale : ${scale}`
  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;
};

// Handl mousenwheel zoom
canvas.onwheel= function(e){
  e.preventDefault();
  context.clearRect(-cw, -ch, 2 * cw, 2 * ch)
  

  let direction = e.deltaY > 0 ? 1 : -1;
  
  
  scrollX += Math.round(e.offsetX * scaleFactor * direction);
  scrollY += Math.round(e.offsetY * scaleFactor * direction);
  scale += scaleFactor * direction;
  
  context.setTransform(1, 0, 0, 1, 0, 0);
  
  context.translate(cx, cy);
  context.scale(scale, scale);
  context.translate(-cx, -cy);
  //render()
}

render()
<canvas id="c" width="350" height="150" style="border: 1px solid red;"></canvas>
<div id="info"></div>
...