【问题标题】:Rotate individual objects in canvas?旋转画布中的单个对象?
【发布时间】:2016-05-01 03:14:42
【问题描述】:

rotate() 函数似乎可以旋转整个绘图区域。有没有办法单独旋转路径?我希望旋转的中心是对象,而不是绘图区域。

使用save()restore() 仍然会使旋转考虑到整个绘图区域。

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.save();
context.fillStyle = 'red';
context.rotate(0.35);
context.fillRect(40,40, 100, 100);
context.restore();

context.save();
context.fillStyle = 'blue';
context.rotate(0.35);
context.fillRect(200, 40, 100, 100);
context.restore();
<canvas id="canvas" width="500" height="500"></canvas>

【问题讨论】:

    标签: javascript canvas rotation


    【解决方案1】:

    使用本地空间

    而不是在您希望他们在其本地空间中围绕其自己的原点绘制所有内容的位置绘制对象。原点位于 (0,0) 处,是对象旋转的位置。

    所以如果你有一个可以用来绘制的矩形

    function drawRect(){
        context.fillRect(200, 40, 100, 100);
    }
    

    改变它,使其在原点绘制

    function drawRect(){
        context.fillRect(-50,-50 , 100, 100);
    }
    

    现在您可以轻松地将其绘制到任何您想要的地方

    从 setTransform 函数开始,因为它会清除任何现有的转换,并且是设置对象中心位置的便捷方法

    ctx.setTransform(1,0,0,1,posX,posY); // clear transform and set center location 
    

    如果你想旋转它然后添加旋转

    ctx.rotate(ang);
    

    并用

    进行缩放
    ctx.scale(scale,scale);
    

    如果你有两个不同的比例,你应该在旋转之前进行缩放。

    现在只需调用绘图函数

    drawRect();
    

    它的中心在 posX,posY 旋转和缩放。

    您可以将它们全部组合成一个具有 x,y 位置、宽度和高度、缩放和旋转的函数。您可以在 setTransform 中包含比例

    function drawRect(x,y,w,h,scale,rotation){
       ctx.setTransform(scale,0,0,scale,x,y);
       ctx.rotate(rotation);
       ctx.strokeRect(-w/2,-h/2,w,h);
    }
    

    它也适用于作为精灵的图像,我将包含一个 alpha

    function drawImage(img,x,y,w,h,scale,rotation,alpha){
       ctx.globalAlpha = alpha;
       ctx.setTransform(scale,0,0,scale,x,y);
       ctx.rotate(rotation);
       ctx.drawImage(img,-img.width/2,-img.height/2,img.width,img.height);
    }
    

    在一台 6 年的笔记本电脑上,每 1/60 秒可以在 Firefox 上绘制 2000 个精灵,每个精灵都经过旋转、缩放、定位,并带有 alpha 渐变。

    无需纠结于前后翻译。只需保留您在其中绘制的所有对象都有自己的原点并通过变换移动该原点。

    更新 丢了演示,所以这里是展示如何在实践中做到这一点。

    只绘制大量旋转、缩放、平移、带字母的矩形。

    使用 setTransform 可以避免保存和恢复,从而节省大量时间

    // create canvas and add resize 
    var canvas,ctx;
    function createCanvas(){
        canvas = document.createElement("canvas"); 
        canvas.style.position = "absolute";
        canvas.style.left     = "0px";
        canvas.style.top      = "0px";
        canvas.style.zIndex   = 1000;
        document.body.appendChild(canvas); 
    }
    function resizeCanvas(){
        if(canvas === undefined){
            createCanvas();
        }
        canvas.width          = window.innerWidth;
        canvas.height         = window.innerHeight; 
        ctx            = canvas.getContext("2d"); 
    }
    resizeCanvas();
    window.addEventListener("resize",resizeCanvas);
    
    
    // simple function to draw a rectangle
    var drawRect = function(x,y,w,h,scale,rot,alpha,col){
      ctx.setTransform(scale,0,0,scale,x,y);
      ctx.rotate(rot);
      ctx.globalAlpha = alpha;
      ctx.strokeStyle = col;
      ctx.strokeRect(-w/2,-h/2, w, h);
    
    }
    
    // create some rectangles in unit scale so that they can be scaled to fit
    // what ever screen size this is in
    var rects = [];
    for(var i = 0; i < 200; i ++){
      rects[i] = {
        x : Math.random(),
        y : Math.random(),
        w : Math.random() * 0.1,
        h : Math.random() * 0.1,
        scale : 1, 
        rotate : 0,
        dr : (Math.random() - 0.5)*0.1, // rotation rate
        ds : Math.random()*0.01,   // scale vary rate
        da : Math.random()*0.01,   // alpha vary rate
        col : "hsl("+Math.floor(Math.random()*360)+",100%,50%)",
      };
    }
    
    // draw everything once a frame
    function update(time){
      var w,h;
      w = canvas.width;  // get canvas size incase there has been a resize
      h = canvas.height;
      ctx.setTransform(1,0,0,1,0,0); // reset transform
      ctx.clearRect(0,0,w,h);  // clear the canvas
      
      // update and draw each rect
      for(var i = 0; i < rects.length; i ++){
        var rec = rects[i];
        rec.rotate += rec.dr;
        drawRect(rec.x * w, rec.y * h, rec.w * w,rec.h * h,rec.scale + Math.sin(time * rec.ds) * 0.4,rec.rotate,Math.sin(time * rec.da) *0.5 + 0.5,rec.col);
      }
      
      requestAnimationFrame(update); // do it all again
    }
    requestAnimationFrame(update);

    【讨论】:

      【解决方案2】:

      画布中的所有转换都是针对整个绘图区域的。如果你想围绕一个点旋转,你将不得不将该点平移到原点,进行旋转并将其平移回来。 this 之类的就是你想要的。

      【讨论】:

      • 但是第二次旋转仍然会影响第一个对象?那不是我想要的。我希望每次旋转只影响之前绘制的对象
      • 转换适用于它们之后发生的事情。因此,当您在调用旋转后调用 fillRect 时,应用的是前面的旋转。如果您想为第二个对象旋转不同的量,只需在调用它之前使用不同的旋转值。
      【解决方案3】:

      使用旋转功能围绕其中心旋转形状的所有点。

      <!DOCTYPE html>
      <html>
      <head>
          <style>
              body
              {
                  margin: 0px;
                  padding: 0px;
                  overflow: hidden;
              }
              canvas
              {
                  position: absolute;
              }
          </style>
      </head>
      <body>
      <canvas id="canvas"></canvas>
      <script>
          var canvas;
          var context;
          canvas = document.getElementById("canvas");
          context = canvas.getContext("2d");
          canvas.width = window.innerWidth;
          canvas.height = window.innerHeight;
          var degreesToRadians = function(degrees)
          {
              return degrees*Math.PI/180;
          }
          var rotate = function(x, y, cx, cy, degrees)
          {
              var radians = degreesToRadians(degrees);
              var cos = Math.cos(radians);
              var sin = Math.sin(radians);
              var nx = (cos * (x - cx)) + (sin * (y - cy)) + cx;
              var ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
              return new Vector2(nx, ny);
          }
          var Vector2 = function(x, y)
          {
              return {x:x,y:y};
          }
          var Shape = function(points, color)
          {
              this.color = color;
              this.points = points;
          };
          Shape.prototype.rotate = function(degrees)
          {
              var center = this.getCenter();
              for (var i = 0; i < this.points.length; i++)
              {
                  this.points[i] = rotate(this.points[i].x,this.points[i].y,center.x,center.y,degrees);
              }
              context.beginPath();
              context.arc(center.x,center.y,35,0,Math.PI*2);
              context.closePath();
              context.stroke();
          }
          Shape.prototype.draw = function()
          {
              context.fillStyle = this.color;
              context.strokeStyle = "#000000";
              context.beginPath();
              context.moveTo(this.points[0].x, this.points[0].y);
              for (var i = 0; i < this.points.length; i++)
              {
                  context.lineTo(this.points[i].x, this.points[i].y);
                  //context.fillText(i+1, this.points[i].x, this.points[i].y);
              }
              context.closePath();
              context.fill();
              context.stroke();
          }
          Shape.prototype.getCenter = function()
          {
              var center = {x:0,y:0};
              for (var i = 0; i < this.points.length; i++)
              {
                  center.x += this.points[i].x;
                  center.y += this.points[i].y;
              }
              center.x /= this.points.length;
              center.y /= this.points.length;
              return center;
          }
          Shape.prototype.translate = function(x, y)
          {
              for (var i = 0; i < this.points.length; i++)
              {
                  this.points[i].x += x;
                  this.points[i].y += y;
              }
          }
          var Rect = function(x,y,w,h,c)
          {
              this.color = c;
              this.points = [Vector2(x,y),Vector2(x+w,y),Vector2(x+w,y+h),Vector2(x,y+h)];
          }
          Rect.prototype = Shape.prototype;
          var r = new Rect(50, 50, 200, 100, "#ff0000");
          r.draw();
          r.translate(300,0);
          r.rotate(30);
          r.draw();
      
      </script>
      </body>
      </html>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-10
        • 2013-02-23
        • 1970-01-01
        • 2014-07-11
        • 2021-11-05
        • 1970-01-01
        相关资源
        最近更新 更多