Giacomo M
Giacomo M

Reputation: 4723

canvas rotates of 180 degrees instead of 90 when I use context.rotate

UPDATE - PART II
I noticed the problem occurs when I do ajax call I do before rotating the image.
I do this:

$.ajax({
    type:'POST',
    beforeSend: function(request) {
        request.setRequestHeader("Authorization", "Bearer {{ app('request')->token }}");
    },
    url: "my-url-to-rotate-image-on-server/90",
    success:function(data) {
        $('#my_id').rotate(90); // the rotate function is the code I wrote below
    },
    error: function(data) { }
});

If i remove the ajax call and just rotate the image, it works.


I need to rotate an image of 90 degrees, but the image rotates of 180.
This is the code (a jquery plugin) I found on the web and I use:

var p = this.get(0);
p.angle = 90;

if (p.angle >= 0) {
    var rotation = Math.PI * p.angle / 180;
} else {
    var rotation = Math.PI * (360+p.angle) / 180;
}
var costheta = Math.cos(rotation);
var sintheta = Math.sin(rotation);

var canvas = document.createElement('canvas');
var onLoad = false;
if (!p.oImage) {
    canvas.oImage = new Image();
    canvas.oImage.src = p.src;
    canvas.oImage.width = p.width;
    canvas.oImage.height = p.height;
    onLoad = true;
} else {
    canvas.oImage = p.oImage;
}

canvas.style.width = canvas.width = Math.abs(costheta*canvas.oImage.width) + Math.abs(sintheta*canvas.oImage.height);
canvas.style.height = canvas.height = Math.abs(costheta*canvas.oImage.height) + Math.abs(sintheta*canvas.oImage.width);

var context = canvas.getContext('2d');
context.save();
if (rotation <= Math.PI/2) {
    context.translate(sintheta*canvas.oImage.height,0);
} else if (rotation <= Math.PI) {
    context.translate(canvas.width,-costheta*canvas.oImage.height);
} else if (rotation <= 1.5*Math.PI) {
    context.translate(-costheta*canvas.oImage.width,canvas.height);
} else {
    context.translate(0,-sintheta*canvas.oImage.width);
}
context.rotate(rotation);

if (onLoad) {
    canvas.oImage.onload = function() {
        context.drawImage(canvas.oImage, 0, 0, canvas.oImage.width, canvas.oImage.height);
        context.restore();
    };
} else {
    context.drawImage(canvas.oImage, 0, 0, canvas.oImage.width, canvas.oImage.height);
    context.restore();
}

What I get is the canvas dimensions are correctly rotated (I mean, the old width becomes the new height and viceversa), but the image is rotated of 180 degrees instead of 90.

I do not know howto debug it.

UPDATE
I noticed that the strange behviour occurs just when the image src is an http url.
If the image src is blob, the code works perfectly.

Upvotes: 1

Views: 1467

Answers (3)

Blindman67
Blindman67

Reputation: 54089

The code you have taken from the net (which should come with a reference / attribution) is very old school (at least 5 years old). On top of that the code is very poor quality.

Note that 2d context translate, scale, rotate and transform are cumulative. If you call ctx.rotate(Math.PI/2) (90 deg) and then call it again ctx.rotate(Math.PI/2) you have in effect rotated Math.PI (180 deg). However the code creates a new canvas each time so this will not be the cause of a double rotation. Why this happens is not evident in the code.

Rotation on the canvas is clockwise. 0 Deg is at 3 o'clock.

Load and rotate 90deg

Separate concerns and use modern JS. Loading the image and displaying the image should be separate functions

First a function to rotate image and fit the canvas

// Rotates image 90 and sets canvas size to fit rotated image
function drawImageRot90(ctx, image) {
    ctx.canvas.style.width = (ctx.canvas.width = image.height) + "px";
    ctx.canvas.style.height = (ctx.canvas.height = image.width) + "px";
    ctx.setTransform(0, 1, -1, 0, ctx.canvas.width, 0);  // overwrite existing transform
    ctx.drawImage(image, 0, 0);
    ctx.setTransform(1, 0, 0, 1, 0, 0);                  // reset default transform
}

Then a function to load the image. The function returns a promise that will pass the image when loaded.

// Return promise to provide loaded image as described in details AKA `p`
function loadImage(details) {
    return new Promise(loaded => {
        if (!details.oImage) {
            const img = new Image;
            img.src = details.src
            img.addEventListener("load",() => {
                    img.width = details.width;
                    img.height = details.height;
                    loaded(img);
                }, {once: true}
            );
        } else {
            loaded(details.oImage);
        }
    });
}

Putting it together

loadImage(this.get(0))
    .then((img) => {
        const canvas = document.createElement("canvas");
        drawImageRot90(canvas.getContext("2d"), img);
    })
    

Function to rotate in steps of 90

function drawImageRot(ang, ctx, image) { // ang must be 0, 90, 180, 270 or will default 180
    if (ang === 90 || ang === 270) {
        ctx.canvas.style.width = (ctx.canvas.width = image.height) + "px";
        ctx.canvas.style.height = (ctx.canvas.height = image.width) + "px";
        if (ang === 90) { ctx.setTransform(0, 1, -1, 0, ctx.canvas.width, 0) } 
        else { ctx.setTransform(0, -1, 1, 0, 0, ctx.canvas.height) }
    } else {
        ctx.canvas.style.width = (ctx.canvas.width = image.width) + "px";
        ctx.canvas.style.height = (ctx.canvas.height = image.height) + "px";
        if (ang === 0) { ctx.setTransform(1, 0, 0, 1, 0, 0) }
        else { ctx.setTransform(-1, 0, 0, -1, ctx.canvas.width, ctx.canvas.height) }
    }
    ctx.drawImage(image, 0, 0);
    ctx.setTransform(1, 0, 0, 1, 0, 0);   // reset default transform
}

Upvotes: 1

John
John

Reputation: 3785

0 degrees points toward East when using cos(0), sin(0) AND not North which is why you think this is not working when it is in fact correct.

When you add 90 degree to East it correctly points South, giving the impression it has rotated 180 degrees.

If you want 0 degree to point North. We know it out by 90 degree then it would be a matter of subtracting the angle by 90 degree.

Where you have

 p.angle = 90;

Replace with...

 const POINT_NORTH = -90;
 p.angle = POINT_NORTH + 90;

Upvotes: 0

enhzflep
enhzflep

Reputation: 13099

window.addEventListener('load', onDocLoaded, false);

function onDocLoaded(evt)
{
	let img = document.querySelector('img');
	let width = img.naturalWidth, height=img.naturalHeight;
	let canvas = document.createElement('canvas');
	canvas.width = height;
	canvas.height = width;
	let ctx = canvas.getContext('2d');
	ctx.rotate(Math.PI/2);
	ctx.translate(0,-height);
	ctx.drawImage(img,0,0);
	document.body.appendChild(canvas);
}
body
{
	background-color: #333;
}
<img id='theImage' src='https://i.sstatic.net/Zqh6Am.png'/>

Upvotes: 0

Related Questions