3

I have a html5 canvas. I want to draw/rotate the image in the canvas and image size does not change.I set width/height of canvas base on size of image.I have a problem to rotate an image.َ After rotate image,the red triangles was crop. In below link a example showing this in problem.Please help. http://jsfiddle.net/zsh64/6ZsCz/76/

 var canvas = document.getElementById("canvas");
      var ctx = canvas.getContext("2d");
      var angleInDegrees = 0;
      var image = document.createElement("img");
      image.onload = function () {
          canvas.width = image.width;
          canvas.height = image.height;
          ctx.drawImage(image, 0,0);
      };
      image.src = "Images/rotate.png";
      $("#R").click(function () {
          angleInDegrees += 90;
          drawRotated(angleInDegrees);
      });
      $("#L").click(function () {
          angleInDegrees -= 90;
          drawRotated(angleInDegrees);
      });
      function drawRotated(degrees) {
          ctx.clearRect(0, 0, canvas.width, canvas.height);
          ctx.save();
          ctx.translate(canvas.width / 2, canvas.height / 2);
          ctx.rotate(degrees * Math.PI / 180);
          ctx.drawImage(image, -image.width / 2, -image.width / 2);
          ctx.restore();
      }
kangax
  • 38,898
  • 13
  • 99
  • 135
mks
  • 45
  • 1
  • 5
  • 1
    Well, if I take a piece of a4 paper in portrait mode and I measure it's dimensions, I'd tell you it was 210x297mm. If on the other hand, I measured the dimensions of the same piece of paper in landscape mode, I'd tell you that it was 297x210mm. See where I'm going here? The size of the bounding-box of your image is orientation-dependant. With integer angles of 90°, you've only got 2 orientations to consider - it's this way, or its that way. (you can simply swap the dimensions - no need for any trig functions) – enhzflep Oct 22 '13 at 13:48

3 Answers3

4

If you rotate your image, you can recalculate the boundingbox needed to contain it.

enter image description hereenter image description here

This code will takes width/height/rotation angle and returns the new bounding box size:

function newSize(w,h,a){
    var rads=a*Math.PI/180;
    var c = Math.cos(rads);
    var s = Math.sin(rads);
    if (s < 0) { s = -s; }
    if (c < 0) { c = -c; }
    size.width = h * s + w * c;
    size.height = h * c + w * s ;
}

Here is example code and a Fiddle: http://jsfiddle.net/m1erickson/h65yr/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <style>
        #containerDiv{
          border: 1px solid red;
          position:absolute;
          top:100px;
          left:100px;
        }
        #canvas{
          border: 1px solid green;
        }
    </style>
    <script>

$(function(){
         var canvas=document.getElementById("canvas");
         var ctx=canvas.getContext("2d");
         var imgWidth=200;
         var imgHeight=300;
         var size={width:imgWidth, height:imgHeight};
         var rotation=0;
         var deg2Rad=Math.PI/180;
         var count1=0;
         var count2=0;

          var img=new Image();
          img.onload=function(){
              imgWidth=img.width;
              imgHeight=img.height;
              size={width:imgWidth, height:imgHeight};
              draw();
          }
          img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/Rotate.png";

      function draw(){
         canvas.width=size.width;
         canvas.height=size.height; 

         // calculate the centerpoint of the canvas
         var cx=canvas.width/2;
         var cy=canvas.height/2;
         var info=document.getElementById("info");
         info.innerHTML="canvas size: "+(count1++)+": "+cx+" / "+cy;

         // draw the rect in the center of the newly sized canvas
         ctx.clearRect(0,0,canvas.width,canvas.height);
         ctx.fillStyle="rgba(216,216,150,1.0)";
         ctx.translate(cx,cy);
         ctx.rotate(rotation * deg2Rad);
         ctx.drawImage(img,-imgWidth/2,-imgHeight/2);
      }

      document.getElementById("rotate").addEventListener("click", rotateClicked, false);

      function rotateClicked(e){
        rotation+=30;
        draw();
      }

      document.getElementById("resize").addEventListener("click", resizeClicked, false);

      function resizeClicked(e){
        rotation+=30;
        newSize(imgWidth,imgHeight,rotation);
        draw();
      }

      function newSize(w,h,a){
        var rads=a*Math.PI/180;
        var c = Math.cos(rads);
        var s = Math.sin(rads);
        if (s < 0) { s = -s; }
        if (c < 0) { c = -c; }
        size.width = h * s + w * c;
        size.height = h * c + w * s ;
    }

});

    </script>

</head>

<body>
<button id="rotate">Rotate without resize</button>
<button id="resize">Resize with resize</button>
<p id=info></p>

<div id="containerDiv">
    <canvas id="canvas" width=400 height=400></canvas>
</div>

</body>
</html>
markE
  • 102,905
  • 11
  • 164
  • 176
3

Issues

There are two issues you are facing:

  1. There is not enough room in the canvas to do a rotation
  2. You are drawing the image at center of rotation rather than center of image

Solutions

To properly size your canvas you can use maximum boundary of the rotated rectangle. To find this boundary you can do one of two things:

If you only need to rotate per 90 degrees:

var length = Math.max(img.width, img.height);

If you want to use free angle:

var length = Math.sqrt(img.width * img.width + img.height * img.height);

Then use one of these results to set the size of the canvas:

canvas.width = canvas.height = length; /// both has same length

Next thing is to properly draw in the image. After rotation you need to translate back as the image is always drawn from its upper left corner:

var pivot = length * 0.5;

ctx.save();
ctx.translate(pivot, pivot);
ctx.rotate(degrees * Math.PI / 180);
ctx.drawImage(image, -image.width * 0.5, -image.height * 0.5);
ctx.restore();

It's important you use the pivot of the image to offset it back as the image will here be of a different size than canvas.

The result from this LIVE DEMO here:

Bounds of rotated rectangle

Community
  • 1
  • 1
1

change the parameter of drawImage

ctx.drawImage(image, -image.width / 2, -image.height/ 2);
Edward Lee
  • 951
  • 5
  • 14
  • Or (to be more consistent with the .translate method call): `ctx.drawImage(image, -canvas.width / 2, -canvas.height / 2);` – devnull69 Oct 22 '13 at 14:13