[javascript] HTML5 Canvas: Zooming

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. :(

This question is related to javascript canvas

The answer is


Canvas zoom and pan

_x000D_
_x000D_
<!DOCTYPE html>_x000D_
<html>_x000D_
<body>_x000D_
_x000D_
<canvas id="myCanvas" width="" height=""_x000D_
style="border:1px solid #d3d3d3;">_x000D_
Your browser does not support the canvas element._x000D_
</canvas>_x000D_
_x000D_
<script>_x000D_
console.log("canvas")_x000D_
var ox=0,oy=0,px=0,py=0,scx=1,scy=1;_x000D_
var canvas = document.getElementById("myCanvas");_x000D_
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;} } _x000D_
_x000D_
canvas.onmouseup=()=>{canvas.onmousemove=null;}_x000D_
canvas.onwheel =(e)=>{let bfzx,bfzy,afzx,afzy;[bfzx,bfzy]=StoW(e.x,e.y);scx-=10*scx/e.deltaY;scy-=10*scy/e.deltaY;_x000D_
[afzx,afzy]=StoW(e.x,e.y);_x000D_
ox+=(bfzx-afzx);_x000D_
oy+=(bfzy-afzy);_x000D_
}_x000D_
var ctx = canvas.getContext("2d");_x000D_
_x000D_
function draw(){_x000D_
window.requestAnimationFrame(draw);_x000D_
ctx.clearRect(0,0,canvas.width,canvas.height);_x000D_
for(let i=0;i<=100;i+=10){_x000D_
let sx=0,sy=i;_x000D_
let ex=100,ey=i;_x000D_
[sx,sy]=WtoS(sx,sy);_x000D_
[ex,ey]=WtoS(ex,ey);_x000D_
ctx.beginPath();_x000D_
ctx.moveTo(sx, sy);_x000D_
ctx.lineTo(ex, ey);_x000D_
ctx.stroke();_x000D_
}_x000D_
for(let i=0;i<=100;i+=10){_x000D_
let sx=i,sy=0;_x000D_
let ex=i,ey=100;_x000D_
[sx,sy]=WtoS(sx,sy);_x000D_
[ex,ey]=WtoS(ex,ey);_x000D_
ctx.beginPath();_x000D_
ctx.moveTo(sx, sy);_x000D_
ctx.lineTo(ex, ey);_x000D_
ctx.stroke();_x000D_
}_x000D_
}_x000D_
draw()_x000D_
function WtoS(wx,wy){_x000D_
let sx=(wx-ox)*scx;_x000D_
let sy=(wy-oy)*scy;_x000D_
return[sx,sy];_x000D_
}_x000D_
function StoW(sx,sy){_x000D_
let wx=sx/scx+ox;_x000D_
let wy=sy/scy+oy;_x000D_
return[wx,wy];_x000D_
}_x000D_
_x000D_
</script>_x000D_
_x000D_
</body>_x000D_
</html>
_x000D_
_x000D_
_x000D_


Just 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>

Works perfect for me with zooming and mouse movement.. you can customize it to mouse wheel up & down Njoy!!!

Here is fiddle for this Fiddle


One option is to use css zoom feature:

$("#canvas_id").css("zoom","x%"); 

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).


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.