HTML5 Canvas: Zooming
Asked Answered
B

7

72

Is there any easy way how to zoom in and back out in canvas (JavaScript)? Basically I have a 400x400px canvas and I'd like to be able to zoom in with 'mousedown' (2x) and go back with 'mouseup'.

Spent last two days googling, but no luck so far. :(

Brunn answered 6/8, 2010 at 4:21 Comment(0)
T
85

Building on the suggestion of using drawImage you could also combine this with scale function.

So before you draw the image scale the context to the zoom level you want:

ctx.scale(2, 2) // Doubles size of anything draw to canvas.

I've created a small example here http://jsfiddle.net/mBzVR/4/ that uses drawImage and scale to zoom in on mousedown and out on mouseup.

Tem answered 6/8, 2010 at 9:15 Comment(6)
WARNING! If you don't care about your images getting pixelated this will work fine. Otherwise you should be multiplying all of your sizes and movement data by a ratio. You could store it in a camera object and output a ratio based upon the zoom level.Swithin
O and in theory you should be able to fix pixelation by rendering the image to a large size then it actually is.Swithin
Also keep in mind that everything scales up, including borders. If you want to increase the size of shapes without thickening the outlines, you'll have to multiply their width, height and position manually as you draw them.Indiscernible
i want to zoom a pdf on a canvas with mouse wheel. Can I use canvas for itSaturnalia
@AshBlue do you have an example using your method?Sombrero
@Dave Not off hand, but I generally create a Camera object that stores the current zoom data and then uses those details to determine how the image is displayed.Swithin
C
40

Try this out:

<!DOCTYPE HTML>
<html>

<head>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
  <style>
    body {
      margin: 0px;
      padding: 0px;
    }
    
    #wrapper {
      position: relative;
      border: 1px solid #9C9898;
      width: 578px;
      height: 200px;
    }
    
    #buttonWrapper {
      position: absolute;
      width: 30px;
      top: 2px;
      right: 2px;
    }
    
    input[type="button"] {
      padding: 5px;
      width: 30px;
      margin: 0px 0px 2px 0px;
    }
  </style>
  <script>
    function draw(scale, translatePos) {
      var canvas = document.getElementById("myCanvas");
      var context = canvas.getContext("2d");

      // clear canvas
      context.clearRect(0, 0, canvas.width, canvas.height);

      context.save();
      context.translate(translatePos.x, translatePos.y);
      context.scale(scale, scale);
      context.beginPath(); // begin custom shape
      context.moveTo(-119, -20);
      context.bezierCurveTo(-159, 0, -159, 50, -59, 50);
      context.bezierCurveTo(-39, 80, 31, 80, 51, 50);
      context.bezierCurveTo(131, 50, 131, 20, 101, 0);
      context.bezierCurveTo(141, -60, 81, -70, 51, -50);
      context.bezierCurveTo(31, -95, -39, -80, -39, -50);
      context.bezierCurveTo(-89, -95, -139, -80, -119, -20);
      context.closePath(); // complete custom shape
      var grd = context.createLinearGradient(-59, -100, 81, 100);
      grd.addColorStop(0, "#8ED6FF"); // light blue
      grd.addColorStop(1, "#004CB3"); // dark blue
      context.fillStyle = grd;
      context.fill();

      context.lineWidth = 5;
      context.strokeStyle = "#0000ff";
      context.stroke();
      context.restore();
    }

    window.onload = function() {
      var canvas = document.getElementById("myCanvas");

      var translatePos = {
        x: canvas.width / 2,
        y: canvas.height / 2
      };

      var scale = 1.0;
      var scaleMultiplier = 0.8;
      var startDragOffset = {};
      var mouseDown = false;

      // add button event listeners
      document.getElementById("plus").addEventListener("click", function() {
        scale /= scaleMultiplier;
        draw(scale, translatePos);
      }, false);

      document.getElementById("minus").addEventListener("click", function() {
        scale *= scaleMultiplier;
        draw(scale, translatePos);
      }, false);

      // add event listeners to handle screen drag
      canvas.addEventListener("mousedown", function(evt) {
        mouseDown = true;
        startDragOffset.x = evt.clientX - translatePos.x;
        startDragOffset.y = evt.clientY - translatePos.y;
      });

      canvas.addEventListener("mouseup", function(evt) {
        mouseDown = false;
      });

      canvas.addEventListener("mouseover", function(evt) {
        mouseDown = false;
      });

      canvas.addEventListener("mouseout", function(evt) {
        mouseDown = false;
      });

      canvas.addEventListener("mousemove", function(evt) {
        if (mouseDown) {
          translatePos.x = evt.clientX - startDragOffset.x;
          translatePos.y = evt.clientY - startDragOffset.y;
          draw(scale, translatePos);
        }
      });

      draw(scale, translatePos);
    };



    jQuery(document).ready(function() {
      $("#wrapper").mouseover(function(e) {
        $('#status').html(e.pageX + ', ' + e.pageY);
      });
    })
  </script>
</head>

<body onmousedown="return false;">
  <div id="wrapper">
    <canvas id="myCanvas" width="578" height="200">
    </canvas>
    <div id="buttonWrapper">
      <input type="button" id="plus" value="+"><input type="button" id="minus" value="-">
    </div>
  </div>
  <h2 id="status">
    0, 0
  </h2>
</body>

</html>

This works perfectly for me with zooming and mouse movement. You can customize it to use the mouse wheel up & down.

Here is a fiddle

Choker answered 24/7, 2012 at 7:15 Comment(2)
Hi GOK, wondering if it's possible apply your code to an image instead of your own drawing? I've tried the below code but it doesn't allow me to drag. May I know what's wrong with it? ` var imageObj = new Image(); imageObj.onload = function() { context.drawImage(imageObj, 69, 50); }; imageObj.src = 'html5canvastutorials.com/demos/assets/darth-vader.jpg';`Annora
Hi GOK, also wondering if you have a solution to zooming with an offset to the translatePos so that the view remains centered. Right now if you change the offset and zoom, it will zoom on the center of the image and not where your current center of the canvas is. I can't find a way to update the translatePos after rescaling to keep the zoom centered properly. Any suggestion ?Glandule
F
8

Canvas zoom and pan

var ox = 0,
  oy = 0,
  px = 0,
  py = 0,
  scx = 1,
  scy = 1;
var canvas = document.getElementById("myCanvas");
canvas.onmousedown = (e) => {
  px = e.x;
  py = e.y;
  canvas.onmousemove = (e) => {
    ox -= e.x - px;
    oy -= e.y - py;
    px = e.x;
    py = e.y;
  };
};

canvas.onmouseup = () => {
  canvas.onmousemove = null;
};
canvas.onwheel = (e) => {
  e.preventDefault();
  let bfzx, bfzy, afzx, afzy;
  [bfzx, bfzy] = StoW(e.x, e.y);
  scx -= (10 * scx) / e.deltaY;
  scy -= (10 * scy) / e.deltaY;
  [afzx, afzy] = StoW(e.x, e.y);
  ox += bfzx - afzx;
  oy += bfzy - afzy;
};
var ctx = canvas.getContext("2d");

function draw() {
  window.requestAnimationFrame(draw);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (let i = 0; i <= 100; i += 10) {
    let sx = 0,
      sy = i;
    let ex = 100,
      ey = i;
    [sx, sy] = WtoS(sx, sy);
    [ex, ey] = WtoS(ex, ey);
    ctx.beginPath();
    ctx.moveTo(sx, sy);
    ctx.lineTo(ex, ey);
    ctx.stroke();
  }
  for (let i = 0; i <= 100; i += 10) {
    let sx = i,
      sy = 0;
    let ex = i,
      ey = 100;
    [sx, sy] = WtoS(sx, sy);
    [ex, ey] = WtoS(ex, ey);
    ctx.beginPath();
    ctx.moveTo(sx, sy);
    ctx.lineTo(ex, ey);
    ctx.stroke();
  }
}
draw();
function WtoS(wx, wy) {
  let sx = (wx - ox) * scx;
  let sy = (wy - oy) * scy;
  return [sx, sy];
}
function StoW(sx, sy) {
  let wx = sx / scx + ox;
  let wy = sy / scy + oy;
  return [wx, wy];
}
<!DOCTYPE html>
<html>
  <body>
    <canvas id="myCanvas" width="" height="" style="border: 1px solid #d3d3d3">
      Your browser does not support the canvas element.
    </canvas>
  </body>
</html>
Faze answered 27/3, 2020 at 14:25 Comment(1)
Panning doesn't adjust to the level of zoom tho.Garmaise
C
7

If you have a source image or canvas element and your 400x400 canvas you want to draw into you can use the drawImage method to achieve zooming.

So for example, the full view might be like this

ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);

And a zoomed view might be like this

ctx.drawImage(img, img.width / 4, img.height / 4, img.width / 2, img.height / 2, 0, 0, canvas.width, canvas.height);

The first parameter to drawImage is the image element or canvas element to draw, the next 4 are the x, y, width and height to sample from the source and the last 4 parameters are the x, y, width and height of the region to draw in the canvas. It will then handle the scaling for you.

You would just need to pick the width and height for the source sample based on the zoom level and the x and y based on where the mouse is clicked minus half the calculated width and height (but you will need to ensure the rectangle isn't out of bounds).

Celibate answered 6/8, 2010 at 5:31 Comment(0)
W
5

IIRC Canvas is a raster style bitmap. it wont be zoomable because there's no stored information to zoom to.

Your best bet is to keep two copies in memory (zoomed and non) and swap them on mouse click.

Wysocki answered 6/8, 2010 at 4:25 Comment(1)
No, this is not your best bet, then you would have to store one for each possible scalePaling
H
0

Here my zoom function :

Zoom(factor_x, factor_y, graph = this.graph, translate_x=this.ctx.canvas.width/2,translate_y=this.ctx.canvas.height/2){
            const new_scale_x = this.scale_x * factor_x
            const new_scale_y = this.scale_y * factor_y
            
            // Update values
            this.translate_x += translate_x * ((this.scale_x - new_scale_x))
            this.translate_y += translate_y * ((this.scale_y - new_scale_y))
            this.scale_x = new_scale_x
            this.scale_y = new_scale_y

            this.ctx.setTransform(this.scale_x, 0, 0, this.scale_y, this.translate_x, this.translate_y) // Transform

            DrawGraph(graph)
        },

For zoom the factor can be 1,3 and for dezoom 0,7 for exemple

Heady answered 7/7, 2023 at 9:3 Comment(0)
C
-2

One option is to use css zoom feature:

$("#canvas_id").css("zoom","x%"); 
Celie answered 16/11, 2017 at 21:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.