如何旋转<canvas>的一部分,而不是整个元素?

Pre*_*fix 2 javascript css html5 canvas rotation

我正在尝试学习一些<canvas>API.我的任务是创建一个简单的模拟式时钟,工作时钟指针(秒,分钟和小时).

时钟框架,面部和指针都使用相同的画布元素绘制.我创建了drawScene()一个每秒运行一次的函数并重绘整个时钟.如果你想更深入地看一下代码,我会将它发布到本帖子底部链接的jsbin中.

目标是drawScene()调用drawClockFace()方法,该方法将当前秒/分钟/小时传递给基于传入时间(即drawSecondHand(currentSecond))绘制手的各个函数.

问题:

如何在不旋转整个画布的情况下旋转画布的各个组件(即我的时钟上的秒针)?我知道我需要根据当前秒数来计算从中心原点绘制线的位置.我只是不确定需要确定"在哪里"画线的宝石计算.

这就是我到目前为止的情况 - 请注意它并不是超级干净,因为我一直在使用它. http://codepen.io/tconroy/pen/BcEbf

小智 5

如何在不旋转整个画布的情况下旋转画布的各个组件(即我的时钟上的秒针)

您可以使用以下方法手动计算角度,而无需每次都旋转画布.下面的演示使用毫秒产生一个平滑的运行时钟(我将在下面显示如果不想要如何丢弃它).

时钟快照

最初,您需要将画布旋转-90度,使其向上0度,而不向右旋转.这使得关于将产生例如12度的0度的角度的生活更容易.绘制主面后,可以执行此旋转.

对于每只手:

  • 根据时间获取角度(此示例中包含的毫秒用于平滑动画)
  • 根据角度渲染线条

而已.

演示

在核心你可以有一个函数,计算小时,分钟和秒的角度的当前时间 - 这个功能也将获得基于毫秒的"中间"的平滑角度(不需要等待一秒钟到更改):

/// somewhere globally
var pi2 = Math.PI * 2;

function timeToAngles() {

    var os = 1 / 60,                  /// mini-step
        time = new Date(),            /// get current time
        h = time.getHours(),          /// get current hour
        m = time.getMinutes(),        /// get current minutes
        s = time.getSeconds(),        /// get current seconds
        ms = time.getMilliseconds(),  /// get current milliseconds
        sa, ma, ha;                   /// for calc. angles

    sa = pi2 * ((s / 60) + (os * ms * 0.001));         /// second's angle
    ma = pi2 * ((m / 60) + (os * s / 60));             /// minute's angle
    ha = pi2 * (((h % 12) / 12) + (( 1 / 12) * m / 60)); /// hour's angle

    return {
        h: ha,
        m: ma,
        s: sa
    }
}
Run Code Online (Sandbox Code Playgroud)

现在只需将这些角度提供给渲染功能:

(function loop() {
    renderClock()
    requestAnimationFrame(loop);
})();
Run Code Online (Sandbox Code Playgroud)

如果你想每秒更新替换rAF(你也可以从中移除计算,timeToAngles()但在这种情况下它将是微观的):

(function loop() {
    setTimeout(loop, 1000);
    renderClock()
})();
Run Code Online (Sandbox Code Playgroud)

然后根据您从当前时间获得的角度渲染线条:

function renderClock() {

    var angles = timeToAngles(),     /// get angles
        cx = ctx.canvas.width * 0.5, /// center
        cy = ctx.canvas.width * 0.5,
        lh = cx * 0.5,               /// length of hour's hand
        lm = cx * 0.8,               /// length of minute's hand
        ls = cx * 0.9,               /// length of second' hand
        pos;                         /// end-point of hand

    /// clear and render face
    ctx.clearRect(0, 0, cx*2, cy*2);
    ctx.beginPath();
    ctx.arc(cx, cy, cx - ctx.lineWidth, 0, pi2);

    /// hours
    pos = lineToAngle(cx, cy, lh, angles.h);
    ctx.moveTo(cx, cy);
    ctx.lineTo(pos.x, pos.y);

    /// minutes
    pos = lineToAngle(cx, cy, lm, angles.m);
    ctx.moveTo(cx, cy);
    ctx.lineTo(pos.x, pos.y);

    /// render hours and minutes
    ctx.lineWidth = 5;
    ctx.stroke();
    ctx.beginPath();

    /// seconds
    pos = lineToAngle(cx, cy, ls, angles.s);
    ctx.moveTo(cx, cy);
    ctx.lineTo(pos.x, pos.y);

    ctx.lineWidth = 2;  /// create a variation for seconds hand
    ctx.stroke();
}
Run Code Online (Sandbox Code Playgroud)

该辅助函数根据角度和三角函数计算终点:

function lineToAngle(x, y, length, angle) {
    return {
        x: x + length * Math.cos(angle),
        y: y + length * Math.sin(angle)
    }
}
Run Code Online (Sandbox Code Playgroud)

渲染钟面的附加提示:

如果你正在制作一个时钟,那么这个技巧会非常有用 - 而不是每次更新清除和渲染面部,而是可以渲染面部一次,将画布转换为数据uri并将其设置为背景图像到画布(本身).

这样你只需要重绘双手.在旋转画布-90度之前(如上图所示):

演示

有面孔的时钟作为自已被回报的背景

var dots = 12,          /// generate dots for each hour
    dotPos = cx * 0.85, /// position of dots
    step = pi2 / dots,  /// calc step
    a = 0;              /// angle for loop

ctx.beginPath();

/// create body border
ctx.beginPath();
ctx.arc(cx, cy, cx - ctx.lineWidth - 2, 0, pi2);
ctx.fillStyle = '#000';
ctx.lineWidth = 5;
ctx.stroke();

/// color of hour dots
ctx.fillStyle = '#999';

/// draw the dots    
for(; a < pi2; a += step) {
    var pos = lineToAngle(cx, cy, dotPos, a);
    ctx.beginPath();
    ctx.arc(pos.x, pos.y, 3, 0, pi2);
    ctx.fill();
}

/// create highlighted dots for every 3 hours
a = 0;
step = pi2 / 4;

ctx.fillStyle = '#777';    
for(; a < pi2; a += step) {
    var pos = lineToAngle(cx, cy, dotPos, a);
    ctx.beginPath();
    ctx.arc(pos.x, pos.y, 5, 0, pi2);
    ctx.fill();
}

/// set as background
clock.style.backgroundImage = 'url(' + clock.toDataURL() + ')';
Run Code Online (Sandbox Code Playgroud)

然后在这里开始循环.