Sophivorus
Sophivorus

Reputation: 3083

Keep the image centred after scaling

I have an image drawn in a canvas, and I want to be able to zoom in and zoom out with any scale, while keeping the image in the center. To do this, my approach is to change the scale of the canvas context and re-draw the image, but I need to calculate the top left corner of the new image, or else it won't be centred.

function zoom( canvas, context, scale ) {
    var image = new Image();
    image.src = canvas.toDataURL( 'image/png' );
    canvas.clearRect( 0, 0, canvas.width, canvas.height );
    context.scale( scale, scale );
    var x = ???,
        y = ???;
    context.drawImage( image, x, y );
}

The question is how to calculate x and y in such a way that it works for any scale. I've figured out some special cases, but I can't find the general rule. When the scale is 0.5, the rule to keep the image centred is:

var x = canvas.width / 2,
    y = canvas.height / 2;

When the scale is 2, the rule is:

var x = -canvas.width / 4,
    y = -canvas.height / 4;

And when the scale is 3, the rule is:

var x = -canvas.width / 3,
    y = -canvas.height / 3;

So what is the general rule? Or is there a better approach?

Upvotes: 3

Views: 4799

Answers (2)

Blindman67
Blindman67

Reputation: 54026

For the center.

Best to do it this way. ctx is canvas context

// scale the coordinate system to required scale and set the origin (0,0) 
// to the center of the canvas
ctx.setTransform(scale, 0, 0, scale, ctx.canvas.width * 0.5, ctx.canvas.height * 0.5);
ctx.drawImage(image, image.width * -0.5, image.height * -0.5); // draw the image offset by half

Or you can avoid setting the transformation and just draw the image scaled

// get the position is half of the canvas width minus the scaled width of the image 
var x = (ctx.canvas.width - image.width * scale) * 0.5;
var y = (ctx.canvas.height - image.height * scale) * 0.5;
ctx.drawImage(image, x, y, image.width * scale, image.height * scale); // drw image with scaled width and height 

Or as you want, scale the canvas and keep the origin in the top left corner. Because the canvas does not change its actual size you have to invert the scale change, which just means divide its size by the scale rather than multiply.

ctx.scale(scale, scale);
var x = (ctx.canvas.width / scale - image.width) * 0.5;
var y = (ctx.canvas.height / scale - image.height) * 0.5;
ctx.drawImage(image, x, y);

Upvotes: 11

francojohnc
francojohnc

Reputation: 815

  getWidth() {
        return this.rect.width * this.scale;
    }

    getHeight() {
        return this.rect.height * this.scale;
    }

    setScale(scale) {

        const oldWidth = this.getWidth();
        const oldHeight = this.getHeight();
        this.scale = scale;
        const newWidth = this.getWidth();
        const newHeight = this.getHeight();

        const addedWidth = newWidth - oldWidth;
        const addedHeight = newHeight - oldHeight;

        this.rect.pos.x -= addedWidth / 2;
        this.rect.pos.y -= addedHeight / 2;
    }

Upvotes: 0

Related Questions