B_CooperA
B_CooperA

Reputation: 659

Rotate canvas image while maintaining aspect ratio

I am trying to implement a rotate function for my photo editor. I'm relatively new working with HTML5 elements, especially canvas elements.

Now, the image will rotate after clicking the button but it doesn't do it while maintaining the aspect ratio. To be specific as possible, I have included two images below: one before the rotation button was clicked and one after that.

BEFORE ROTATION: enter image description here

AFTER ROTATION: enter image description here

As you can see, image does indeed rotate but it will crop majority of the image. Am I missing something? Any help would be much appreciated.

Here's what I've tried so far with results shown above:

function rotateImage(degrees, image) {
    var canvas = document.createElement("canvas"),
        ctx = canvas.getContext("2d");

    if(degrees == 90 || degrees == 270) {
        canvas.width = image.height;
        canvas.height = image.width;
    } else {
        canvas.width = image.width;
        canvas.height = image.height;
    }

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    if(degrees == 90 || degrees == 270) {
        ctx.translate(image.height/2,image.width/2);
    } else {
        ctx.translate(image.width/2,image.height/2);
    }
    ctx.rotate(degrees * Math.PI / 180);
    ctx.drawImage(image, -image.width / 2 , -image.height / 2);

    var dataURL = canvas.toDataURL();
    $("#image").attr('src', dataURL);
}

document.getElementById("rotate").addEventListener("click", function() {
var el = this;
var data = el.getAttribute('data-value');
  var angleInDegrees = 0;

  if(data == 90) {
    angleInDegrees += 90 % 360;
  } else {
    if(angleInDegrees == 0) {
      angleInDegrees = 270;
    } else {
      angleInDegrees -= 90;
    }
  }
  // rotate the image based on degree value
  rotateImage(angleInDegrees, document.getElementById("canvas"));
});

EDIT:

Now, it does indeed rotate the image while maintaining the aspect ratio but only do it once. If rotate button is clicked again, it crops about 20% of the original image size. Few more pictures to demonstrate:

BEFORE ROTATION

enter image description here

AFTER ROTATION BUTTON WAS CLICKED ONCE enter image description here

AFTER ROTATION BUTTON WAS CLICKED AGAIN enter image description here

Modified function

function rotateImage(degrees, image) {
    var canvas = document.createElement("canvas"),
        ctx = canvas.getContext("2d");

    if(degrees == 90 || degrees == 270) {
        canvas.width = image.height;
        canvas.height = image.width;
    } else {
        canvas.width = image.width;
        canvas.height = image.height;
    }

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.save();

    if(degrees == 90 || degrees == 270) {
        ctx.translate(image.height/2,image.width/2);
    } else {
        ctx.translate(image.width/2,image.height/2);
    }
    ctx.rotate(degrees * Math.PI / 180);
    ctx.drawImage(image, -image.width / 2 , -image.height / 2);
    ctx.restore();

    var dataURL = canvas.toDataURL();
    image.setAttribute('src', dataURL);
}

Upvotes: 2

Views: 1089

Answers (1)

enxaneta
enxaneta

Reputation: 33024

I've made a few changes to your code.

  1. if(data == "90") instead of if(data == 90) because it's a string
  2. I've added ctx.save() and ctx.restore() around the transformations
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d");


let cw = canvas.width = image.width;
let ch = canvas.height = image.height;

ctx.drawImage(image, 0 , 0);



function rotateImage(degrees, image) {

    if(degrees == 90 || degrees == 270) {
        cw = image.height;
        ch = image.width;
    } else {
        cw = image.width;
        ch = image.height;
    }

    ctx.clearRect(0, 0, cw, ch);
    ctx.save()
    ctx.translate(cw/2,ch/2);
    ctx.rotate(degrees * Math.PI / 180);
    ctx.drawImage(image, -image.width / 2 , -image.height / 2);
    ctx.restore();
    var dataURL = canvas.toDataURL();
    image.setAttribute('src', dataURL);
}

rotate.addEventListener("click", function() {
var el = this;
var data = el.dataset.value; console.log(data)
  var angleInDegrees = 0;

  if(data == "90") {
    angleInDegrees += 90 % 360;
  } else {
    if(angleInDegrees == 0) {
      angleInDegrees = 270;
    } else {
      angleInDegrees -= 90;
    }
  }


  // rotate the image based on degree value
  rotateImage(angleInDegrees, image);
});

Upvotes: 2

Related Questions