【问题标题】:Why is there no way to rotate in canvas.getContext('2d').setTransform(a,b,c,d,e,f) and what is the best to do a rotate为什么没有办法在 canvas.getContext('2d').setTransform(a,b,c,d,e,f) 中旋转,什么是最好的旋转
【发布时间】:2017-09-21 16:36:24
【问题描述】:

我正在使用 HTML5 画布,我正在尝试实现一种使用平移、缩放和旋转在画布上移动图像的方法。

我已经使用 setTransform 进行平移和缩放:

canvas.getContext('2d').setTransform(a,b,c,d,e,f)

这很方便,因为它会丢弃以前应用的变换,然后应用新的变换,因此在缩放等时无需记住以前的状态。

On W3 schools 表示第二个和第三个参数是 skewYskewX,我最初假设它们是旋转 x 和 y。然而,在应用将一些值传递给这些参数的转换之后,它似乎没有旋转 - 它使画布倾斜! (我知道很奇怪:-D)。

谁能告诉我为什么在集合变换中没有旋转(我很感兴趣,因为它看起来很奇怪,而且倾斜对我来说似乎毫无用处),以及围绕中心旋转的最佳方法是什么canvas 同时使用setTransform

【问题讨论】:

    标签: javascript html canvas


    【解决方案1】:

    setTransform 基于 2D 矩阵 (3x3)。这类矩阵用于 2D/3D 投影,目前通常由游戏引擎处理,而不是由制作游戏的程序员处理。

    这些东西有点线性代数和微积分(用于旋转)。

    你不会很喜欢这个,但这是你正在做的事情:

    function degs_to_rads (degs) { return degs / (180/Math.PI); }
    function rads_to_degs (rads) { return rads * (180/Math.PI); }
    

    从这些辅助函数开始,因为虽然我们在度数方面思考得很好,但计算机和数学系统在弧度方面表现更好。

    然后你想开始计算你的旋转:

    var rotation_degs = 45,
        rotation_rads = degs_to_rads(rotation_degs),
    
        angle_sine = Math.sin(rotation_rads),
        angle_cosine = Math.cos(rotation_rads);
    

    然后,根据参数的布局:

    ctx.setTransform(scaleX, skewY, skewX, scaleY, posX, posY);
    

    按以下顺序,重新排列为变换矩阵时:

    //| scaleX, skewX,  posX |
    //| skewY,  scaleY, posY |
    //| 0,      0,      1    |
    

    ...您需要提交以下值:

    ctx.setTransform(angle_cosine, angle_sine, -angle_sine, angle_cosine, x, y);
    // where x and y are now the "centre" of the rotation
    

    这应该让你顺时针旋转。

    边际收益是您应该能够将所有内容乘以您最初想要的比例(但不要将 posX 和 posY 相乘)。

    【讨论】:

    • 为信息干杯,今晚我会玩它并报告我的进度!
    【解决方案2】:

    我根据#Norguard 的回答做了一些测试。

    以下是在画布上绘制精灵的全过程,包括平移、缩放、旋转(在旋转中心)和阿尔法(不透明度):

    var width = sprite.width;
    var height = sprite.height;
    var toX = sprite.transformOriginX * width;
    var toY = sprite.transformOriginY * height;
    // get the sin and cos value of rotate degree
    var radian = sprite.rotate / 180 * Math.PI;
    var sin = Math.sin(radian);
    var cos = Math.cos(radian);
    
    ctx.setTransform(
        cos * sprite.scaleX,
        sin * sprite.scaleX,
        -sin * sprite.scaleY,
        cos * sprite.scaleY,
        sprite.x + toX,
        sprite.y + toY
    );
    ctx.globalAlpha = sprite.alpha;
    ctx.fillStyle = sprite.color;
    ctx.fillRect(-toX, -toY, width, height);
    

    我还做了一个互动展示,你可以玩:

    // prepare the context
    var myCanvas = document.getElementById('myCanvas');
    var ctx = myCanvas.getContext('2d');
    
    // say we have a sprite looks like this
    var sprite = {
      x: 50,
      y: 50,
      width: 50,
      height: 100,
      transformOriginX: 0.5, // the center of sprite width
      transformOriginY: 0.5, // the center of sprite height
      scaleX: 1.5,
      scaleY: 1,
      rotate: 45,
      alpha: 0.5, // opacity
      color: 'red'
    };
    
    function drawSprite() {
    
      var width = sprite.width;
      var height = sprite.height;
      var scaleX = sprite.scaleX;
      var scaleY = sprite.scaleY;
      // get the transform-origin value
      var toX = sprite.transformOriginX * width;
      var toY = sprite.transformOriginY * height;
      // get the sin and cos value of rotate degree
      var radian = sprite.rotate / 180 * Math.PI;
      var sin = Math.sin(radian);
      var cos = Math.cos(radian);
    
      ctx.setTransform(
        cos * scaleX,
        sin * scaleX,
        -sin * scaleY,
        cos * scaleY,
        sprite.x + toX,
        sprite.y + toY
      );
      ctx.globalAlpha = sprite.alpha;
      ctx.fillStyle = sprite.color;
      ctx.fillRect(-toX, -toY, width, height);
      
      if (toShowInfo) {
        ctx.globalAlpha = 1;
        
        ctx.beginPath();
        ctx.moveTo(-toX + width / 2, -toY + height / 2);
        ctx.lineTo(-toX + width / 2, -toY);
        ctx.strokeStyle = 'lime';
        ctx.stroke();
        
        ctx.beginPath();
        ctx.moveTo(-toX + width / 2, -toY + height / 2);
        ctx.lineTo(-toX + width, -toY + height / 2);
        ctx.strokeStyle = 'yellow';
        ctx.stroke();
      }
    }
    
    function draw() { // main launcher
      // rest the ctx
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.clearRect(0, 0, myCanvas.width, myCanvas.height);
      ctx.fillStyle = 'white';
      ctx.font = '12px Arial';
      ctx.textAlign = 'end';
      ctx.textBaseline = 'hanging';
      ctx.fillText('made by Rex Hsu', 395, 5);
      // draw sprite
      drawSprite();
      // draw info
      if (toShowInfo) { drawInfo(); };
    }
    
    function drawInfo() {
    
      var x = sprite.x;
      var y = sprite.y;
      var width = sprite.width;
      var height = sprite.height;
      var toX = sprite.transformOriginX * width;
      var toY = sprite.transformOriginY * height;
    
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.globalAlpha = 1;
      ctx.beginPath();
      ctx.arc(x + toX, y + toY, 3, 0, Math.PI * 2);
      ctx.fillStyle = 'lime';
      ctx.fill();
      
      ctx.font = '12px Arial';
      ctx.textAlign = 'start';
      ctx.textBaseline = 'middle';
      ctx.fillText('center of rotation', x + toX + 10, y + toY + 0);
    
      ctx.beginPath();
      ctx.rect(x, y, width, height);
      ctx.strokeStyle = 'lime';
      ctx.stroke();
    }
    
    function modifySprite() {
    
      var name = this.id;
      var value = this.value;
      
      if (name !== 'color') {
        value *= 1;
      }
      
      sprite[name] = value;
      draw();
    }
    
    // init
    var toShowInfo = true;
    
    document.getElementById('checkbox').onchange = function() {
    
      toShowInfo = !toShowInfo;
      draw();
    };
    
    var propsDom = document.getElementById('props');
    
    for (var i in sprite) {
      var div = document.createElement('div');
      var span = document.createElement('span');
      var input = document.createElement('input');
      
      span.textContent = i + ':';
      input.id = i;
      input.value = sprite[i];
      input.setAttribute('type', 'text');
      input.addEventListener('keyup', modifySprite.bind(input));
    
      div.appendChild(span);
      div.appendChild(input);
      propsDom.appendChild(div);
    }
    
    draw();
    body {
      font-family: monospace;
    }
    
    canvas {
      float: left;
      background-color: black;
    }
    
    div {
      float: left;
      margin: 0 0 5px 5px;
    }
    
    div > div {
      float: initial;
    }
    
    span {
      font-size: 16px;
    }
    
    input[type="text"] {
      margin: 0 0 5px 5px;
      color: #999;
      border-width: 0 0 1px 0;
    }
    <canvas id="myCanvas" width="400" height="400"></canvas>
    <div id="props" style="float: left; width: calc(100% - 400px - 5px);">
      <div style="float: initial;">
        <input type="checkbox" id="checkbox" checked><span>Show origin-shape and the center of rotation</span>
      </div>
    </div>

    【讨论】:

      猜你喜欢
      • 2012-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-28
      • 2020-10-13
      • 2013-06-23
      • 1970-01-01
      相关资源
      最近更新 更多