【问题标题】:HTML5 Canvas rotate functionHTML5 Canvas 旋转功能
【发布时间】:2020-04-13 17:28:13
【问题描述】:
function drawNumbers(){
    var rad, num;

    cx.font= "30px Arial";
    cx.textAlign = "center";
    cx.textBaseline = "middle";

    //numbers around the inner circumference
     for(num=1; num < 13; num++){
    rad = num * Math.PI/6; //angle for every number
     cx.rotate(rad);
     cx.translate(0, -175);
     cx.rotate(-rad);
     cx.fillText(num.toString(),0,0);
     cx.rotate(rad);
     cx.translate(0, 175);
     cx.rotate(-rad);
    }
}

function drawHands(){
    //getting the time
    var time = new Date();
    var hours = time.getHours();
    var minutes = time.getMinutes();
    var seconds = time.getSeconds();

    //setting the radians based on the time
    //hour hand
    hours %= 12;
    hours = (hours * Math.PI/6) + (minutes * Math.PI/360) + (seconds * Math.PI/21600);
    hands(hours, radius * 0.04, radius * 0.5);

    //minute hand
    minutes = (minutes * Math.PI/30) + (seconds * Math.PI/1800);
    hands(minutes, radius * 0.03, radius * 0.65);

    //second hand
    seconds = (seconds * Math.PI/30);
    hands(seconds, radius * 0.01, radius * 0.68);
}

function hands(ang, width, length){
    cx.beginPath();
    cx.lineWidth = width;
    cx.lineJoin = "round";
    cx.lineCap = "round";
    cx.moveTo(0, 0);
    cx.rotate(ang);
    cx.lineTo(0, -length);
    cx.stroke();
    cx.rotate(-ang);
}

我在 W3Schools 中学习 HTML5 画布,教程教授如何制作工作时钟。 1. 我只是不明白额外的旋转在函数中是如何工作的。 2. 应用旋转功能时,是否总是从画布的原点(0, 0) 开始旋转?

【问题讨论】:

    标签: javascript html5-canvas rotatetransform


    【解决方案1】:

    当您调用rotate 函数时,它会旋转整个画布,想象一下拿着一幅画然后倾斜它。它总是发生在原点附近。围绕不同点旋转的方法是先translate整个画布,然后rotate它。

    回到绘画的类比,如果我们旋转了我们的绘画,一旦我们画好了我们的线条,我们就需要将绘画恢复到直立状态。因此我们rotate(-ang)。如果我们进行了翻译,我们也必须以类似的方式撤消我们的翻译。

    旋转以及我们为什么要撤消旋转

    在下面的代码中,您可以看到我正在绘制一个基本黑色矩形,然后调用一个函数将画布旋转 0.5 弧度并绘制另一个矩形两次。我还没有撤消我的旋转,所以第三个矩形实际上旋转了 1 弧度。

    let canvas = document.getElementById("canvas");
    let ctx = canvas.getContext("2d");
    
    function drawRotatedRectangle() {
      ctx.rotate(0.5);
      ctx.fillRect(0, 0, 60, 50);
    }
    
    // Draw base rectangle
    
    ctx.fillRect(0, 0, 60, 50);
    
    // Rotate and draw second rectangle
    
    ctx.fillStyle = "#FF0000";
    
    drawRotatedRectangle();
    
    // rotate and draw third rectangle
    
    ctx.fillStyle = "#00FF00";
    
    drawRotatedRectangle();
    &lt;canvas id="canvas"&gt;&lt;/canvas&gt;

    为了解决这个问题,我们修改了drawRotatedRectangle() 函数以撤消它所做的所有平移和旋转:

    let canvas = document.getElementById("canvas");
    let ctx = canvas.getContext("2d");
    
    function drawRotatedRectangle() {
      ctx.rotate(0.5);
      ctx.fillRect(0, 0, 60, 50);
      ctx.rotate(-0.5);
    }
    
    // Draw base rectangle
    
    ctx.fillRect(0, 0, 60, 50);
    
    // Rotate and draw second rectangle
    
    ctx.fillStyle = "#FF0000";
    
    drawRotatedRectangle();
    
    // rotate and draw third rectangle
    
    ctx.fillStyle = "#00FF00";
    
    drawRotatedRectangle();
    &lt;canvas id="canvas"&gt;&lt;/canvas&gt;

    现在我们看到红色(隐藏)矩形和绿色矩形在同一个角度。

    绕不同点旋转到原点

    为了演示如何围绕与原点不同的位置旋转,我们可以首先翻译上下文原点的位置,然后旋转画布。下面我将原点移动到基本矩形的中心,旋转画布并在基本矩形的顶部绘制一个旋转的矩形。再次按照最近应用的顺序恢复平移和旋转。

    let canvas = document.getElementById("canvas");
    let ctx = canvas.getContext("2d");
    
    function drawRotatedRectangle() {
      // Move origin to center of rectangle
      ctx.translate(30, 25);
      // Rotate 0.5 radians
      ctx.rotate(0.5);
      // Draw rectangle where the center of the rectangle is the origin
      ctx.fillRect(-30, -25, 60, 50);
      // Undo our rotate
      ctx.rotate(-0.5);
      // Undo our translate
      ctx.translate(-30, -25);
    }
    
    // Draw base rectangle
    
    ctx.fillRect(0, 0, 60, 50);
    
    // Rotate and draw second rectangle
    
    ctx.fillStyle = "#FF0000";
    
    drawRotatedRectangle();
    
    // rotate and draw third rectangle
    
    ctx.fillStyle = "#00FF00";
    
    drawRotatedRectangle();
    &lt;canvas id="canvas"&gt;&lt;/canvas&gt;

    编辑 - 处理舍入错误

    正如 Kaiido 在 cmets 中提到的那样,rotate(a) 函数将对给定的输入进行四舍五入,因此简单地执行反向 rotate(-a) 函数不会使您返回到原始转换。

    他们建议的解决方案是使用setTransform() 将变换矩阵设置为所需的位置,在这些示例中,我们只是返回到画布的原始变换,因此我们可以使用单位矩阵:

    ctx.setTransform(1,0,0,1,0,0)

    或者,您也可以使用save()restore() 方法。这些就像将画布上下文的当前状态推送到堆栈一样,当您restore() 时,它将从堆栈中弹出最新状态,将您返回到先前的转换。 This Jakob Jenkov 的文章通过一些示例进一步解释了这种方法。

    【讨论】:

    • 请注意,尽管rotate 会将您传递的值四舍五入,因此rotate(n); rotate(-n); 可能不会以原始旋转结束。如果你知道你当前的转换,那么最好使用绝对的setTransform 方法来重置它,就像这里一样,你的例子都在单位转换矩阵上,所以你可以做ctx.setTransform(1,0,0,1,0,0) 回到那个单位矩阵.
    • 不错,好地方。使用save()restore() 方法以及返回之前的转换和状态可能很有用。
    • 不幸的是,保存和恢复也会保存和恢复一堆无用的状态信息,如样式和剪切区域等,因此会比 setTransform 慢。
    猜你喜欢
    • 2019-05-29
    • 2015-08-06
    • 2022-01-10
    • 2012-02-27
    • 2019-04-06
    • 1970-01-01
    • 2012-02-14
    • 1970-01-01
    • 2018-01-19
    相关资源
    最近更新 更多