Search code examples
javascriptcanvas

How to make a draggable canvas using the translate method?


I'm trying to make a game map move like google maps.

I can't understand why I don't drag to the bottom or up, but only to the left or right, where you can read information about it or see formulas, but please don't throw off links to complex and cumbersome code that I can't even figure out.

My attempt

const canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext('2d');

const drawRects = () => {
  ctx.fillStyle = 'red';
  ctx.fillRect(0, 0, 1000, 1000);
  
  ctx.fillStyle = 'aqua';
  ctx.fillRect(1000, 1000, 1000, 1000);
  
  ctx.fillStyle = 'green';
  ctx.fillRect(0, 1000, 1000, 1000);
  
  ctx.fillStyle = 'magenta';
  ctx.fillRect(1000, 0, 1000, 1000);
}


const mouse = {
  isDown: false,
  isUp: false,
  isPressed: false,
  x: 0,
  y: 0
}

const pos = {
  x: 0,
  y: 0
}


function tick() {
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  
  canvas.width += 0;
  
  if(mouse.isPressed){
    ctx.translate(mouse.x - pos.x, mouse.y - pos.y)
  }
  
  drawRects()
  

  
  requestAnimationFrame(tick)
}

canvas.addEventListener('mousedown', e => {
  pos.x = e.offsetX;
  pos.y = e.offsetY;
  
  
  
  mouse.isDown = true;
  mouse.isUp = false;
  mouse.isPressed = true;
})

canvas.addEventListener('mousemove', e => {
  if(!mouse.isDown) return;
  
  mouse.x = e.offsetX;
  mouse.y = e.offsetY;
})

canvas.addEventListener('mouseup', e => {
  mouse.isDown = false;
  mouse.isUp = true;
})


tick()
body{
  margin: 0;
}
<canvas></canvas>

I will be glad of any help


Solution

  • You need to track the current translation of your canvas, in addition to the new delta. Right now, what's happening is that everything you mousedown, you set pos.x=e.offsetX, pos.y=e.offsetY, mouse.x=e.offsetX, and mouse.y = e.offsetY. Then in your next tick() method, you do ctx.translate(mouse.x - pos.x, mouse.y - pos.y) = ctx.translate(e.offsetX - e.offsetX, e.offsetY - e.offsetY) = ctx.translate(0, 0), which resets your canvas to its starting position.

    So adding an additional object to track the translation

    const canvas = document.querySelector('canvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    const ctx = canvas.getContext('2d');
    
    const drawRects = () => {
      ctx.fillStyle = 'red';
      ctx.fillRect(0, 0, 1000, 1000);
      
      ctx.fillStyle = 'aqua';
      ctx.fillRect(1000, 1000, 1000, 1000);
      
      ctx.fillStyle = 'green';
      ctx.fillRect(0, 1000, 1000, 1000);
      
      ctx.fillStyle = 'magenta';
      ctx.fillRect(1000, 0, 1000, 1000);
    }
    
    
    const mouse = {
      isDown: false,
      isUp: false,
      isPressed: false,
      x: 0,
      y: 0
    }
    
    const pos = {
      x: 0,
      y: 0
    }
    
    const trans = {
        x: 0,
        y: 0
    }
    
    
    function tick() {
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      
      canvas.width += 0;
      
      if(mouse.isPressed){
        // translate the canvas by its base position + the delta of the current mouse drag event
        ctx.translate(trans.x + (mouse.x - pos.x), trans.y + (mouse.y - pos.y));
      }
      
      drawRects()
      
      requestAnimationFrame(tick)
    }
    
    canvas.addEventListener('mousedown', e => {
      pos.x = e.offsetX;
      pos.y = e.offsetY;
      
      mouse.isDown = true;
      mouse.isUp = false;
      mouse.isPressed = true;
    })
    
    canvas.addEventListener('mousemove', e => {
      if(!mouse.isDown) return;
      
      mouse.x = e.offsetX;
      mouse.y = e.offsetY;
    
        
    })
    
    canvas.addEventListener('mouseup', e => {
      mouse.isDown = false;
      mouse.isUp = true;
    
      // save off the new translation when we mouse-up
      trans.x += (mouse.x - pos.x);
      trans.y += (mouse.y - pos.y);
    
      // need to reset the mouse's starting and current positions before we start another drag event
      pos.x = 0, pos.y = 0, mouse.x = 0, mouse.y = 0;
    })
    
    tick();
    body{
      margin: 0;
    }
    <canvas></canvas>