Canvas旋转后的图像裁剪

3

我有一个HTML5画布。我想在画布中绘制/旋转图像,但图像的大小不会改变。我根据图像大小设置画布的宽度/高度。我在旋转图像时遇到了问题。旋转后,红色三角形被裁剪了。下面的链接是一个示例,展示了这个问题。请帮忙解决。

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();
      }

1
如果我拿一张A4纵向放置的纸张并测量其尺寸,我会告诉你它是210x297毫米。另一方面,如果我测量同一张横向放置的纸张的尺寸,我会告诉你它是297x210毫米。明白我的意思了吗?图像边界框的大小取决于方向。在90°的整数角度下,您只需要考虑两个方向 - 这样或那样。(您可以简单地交换尺寸-无需任何三角函数) - enhzflep
3个回答

4

如果您旋转图像,可以重新计算包含它所需的边界框。

enter image description hereenter image description here

此代码将获取宽度/高度/旋转角度并返回新的边界框大小:

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 ;
}

这里是示例代码和 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>

3

问题

您面临两个问题:

  1. 画布上没有足够的空间进行旋转
  2. 您正在以旋转中心而不是图像中心绘制图像

解决方案

要正确调整画布大小,您可以使用旋转矩形的最大边界。要找到此边界,可以执行以下两个操作之一:

如果您只需要每次旋转90度:

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

如果您想使用自由角度:
var length = Math.sqrt(img.width * img.width + img.height * img.height);

然后使用其中一个结果来设置画布的大小:
canvas.width = canvas.height = length; /// both has same length

接下来,需要正确地绘制图像。旋转后,需要将其平移回来,因为图像始终从其左上角绘制:

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();

重要的是使用图像的中心点将其偏移回来,因为该图像在画布上的大小与原始大小不同。
这里的LIVE DEMO的结果:

Bounds of rotated rectangle


1
改变drawImage的参数。
ctx.drawImage(image, -image.width / 2, -image.height/ 2);

或者(为了更符合.translate方法的调用):ctx.drawImage(image, -canvas.width / 2, -canvas.height / 2); - devnull69

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接