如何旋转< canvas>的片段,而不是整个元素? [英] How can I rotate a segment of <canvas>, not the entire element?

查看:120
本文介绍了如何旋转< canvas>的片段,而不是整个元素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在想学习一些< canvas> API。我的任务是创建一个简单的模拟时钟,用工作时钟指针(秒,分和小时)。



时钟框架,相同的canvas元素。我创建了一个 drawScene()函数,它每秒运行并重绘整个时钟。如果你想更深入地看代码,我会把它发布到一个jsbin链接在这篇文章的底部。



目标是为 drawScene()方法调用 drawClockFace()方法,它将当前秒/分/小时传递给绘制基于传递的时间(即 drawSecondHand(currentSecond))。



问题



如何在不旋转整个画布的情况下旋转画布的各个组件(即我时钟上的秒针)?我知道我需要计算在哪里绘制从中心出发的线,基于当前秒。我只是不确定gemoetric计算需要确定在哪里画线。



这里是我到目前为止 - 注意它不是超级干净,因为我一直在用它。


在核心,您可以拥有一个函数,用于计算当前时间为小时,分钟和秒的角度 - 此函数也将获得平滑之间的角度,以毫秒为单位(不需要等待一整秒钟更改):

  // /全球某地
var pi2 = Math.PI * 2;

function timeToAngles(){

var os = 1/60,/// mini-step
time = new Date(),/// get current时间
h = time.getHours(),///获取当前时间
m = time.getMinutes(),///获取当前分钟
s = time.getSeconds(),/// get当前秒
ms = time.getMilliseconds(),///获取当前毫秒
sa,ma,ha; /// for calc。角度

sa = pi2 *((s / 60)+(os * ms * 0.001)); /// second's angle
ma = pi2 *((m / 60)+(os * s / 60)); ///分钟角度
ha = pi2 *(((h%12)/ 12)+((1/12)* m / 60) ///小时的角度

return {
h:ha,
m:ma,
s:sa
}
}

现在,只需要将这些角度输入到渲染函数:

 (function loop(){
renderClock()
requestAnimationFrame(loop);
})

如果您只想每秒更新一次,请将rAF替换为code> timeToAngles()但在此上下文中将是微观的):

  function loop(){
setTimeout(loop,1000);
renderClock()
})();

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

  function renderClock(){

var angles = timeToAngles(),/// get angles
cx = ctx.canvas .width * 0.5,/// center
cy = ctx.canvas.width * 0.5,
lh = cx * 0.5,///小时手的长度
lm = cx * 0.8, ///分钟的手长度
ls = cx * 0.9,///秒钟的长度
pos; /// end-point of hand

///清除和渲染面
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);

///呈现小时和分钟
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; ///为秒针创建变体
ctx.stroke();
}

此帮助函数根据角度和三角函数计算终点: p>

  function lineToAngle(x,y,length,angle){
return {
x:x + length * Math .cos(angle),
y:y + length * Math.sin(angle)
}
}

呈现钟面的其他提示:



如果您正在制作时钟,是非常有帮助的 - 而不是清除和渲染的脸每次更新,你可以改为渲染的面部一次,将画布转换为一个数据uri,并将其设置为背景图像到画布(本身)。



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



演示



  var dots = 12,///每小时生成点数
dotPos = cx * 0.85,///点的位置
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();

///小时点的颜色
ctx.fillStyle ='#999';

///画圆点
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();
}

///每3小时创建一个突出显示的点
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();
}

///设置为背景
clock.style.backgroundImage ='url('+ clock.toDataURL()+')';

然后在这里开始循环。


I'm trying to learn some of the <canvas> API right now. I've tasked myself with creating a simple analog style clock with working clock hands (second, minute and hour).

The clock frame, face and hands are all drawn with the same canvas element. I've created a drawScene() function which runs every second and redraws the entire clock. If you want to look more in-depth at the code I will post it into a jsbin linked at the bottom of this post.

The goal is for the drawScene() method to call the drawClockFace() method, which passes the current second / minute / hour to individual functions that draw the hand based on the passed in time (ie drawSecondHand(currentSecond)).

question:

how do I rotate individual components of a canvas (ie the second hand on my clock) without rotating the entire canvas? I know I need to calculate where to draw the line from the center origin out, based on what the current second is. I'm just not sure the gemoetric calculation needed to determined "where" to draw the line.

Here's what I have so far -- note it's not super clean because I've been putzing with it. http://codepen.io/tconroy/pen/BcEbf

解决方案

How do I rotate individual components of a canvas (ie the second hand on my clock) without rotating the entire canvas?

You can use the following approach to calculate the angles manually without rotating the canvas each time. The demos below produces a smooth running clock utilizing milliseconds (I'll show below how to drop this if not wanted).

Initially you would want to rotate the canvas -90 degrees to have 0 degrees point up and not to the right. This makes life easier in regards to the angles which will produce for example 0 degree for 12 o'clock. You can do this rotation after drawing the main face.

For each hand:

  • Get their angles based on time (milliseconds included in this example for smooth animation)
  • Render the lines based on angles

That's it.

Demo

At the core you can have a function which calculates current time into angles for the hour, minutes and seconds - this function will also get the smooth angles for "in-between" based on milliseconds (doesn't need to wait a whole second to change):

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

Now it's simply a matter of feeding these angles to your render function:

(function loop() {
    renderClock()
    requestAnimationFrame(loop);
})();

If you want to update only per second replace rAF with (you could also remove the calculation from the timeToAngles() but it will be microscopic in this context):

(function loop() {
    setTimeout(loop, 1000);
    renderClock()
})();

Then render lines based on the angles you get from current time:

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

This helper function calculates the end-point based on angle and trigonometry:

function lineToAngle(x, y, length, angle) {
    return {
        x: x + length * Math.cos(angle),
        y: y + length * Math.sin(angle)
    }
}

Additional tip for rendering the clock face:

If you are making a clock then this tip can be very helpful - instead of clearing and rendering the face each update you can instead render the face once, convert the canvas to a data-uri and set that as a background image to canvas (itself).

This way you only need to redraw the hands. Before rotating the canvas -90 degrees (as shown above):

Demo

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

Then start the loop here.

这篇关于如何旋转&lt; canvas&gt;的片段,而不是整个元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆