我正在尝试学习一些<canvas>
API。我的任务是创建一个简单的模拟式时钟,工作时钟指针(秒,分钟和小时)。
时钟框架,面部和指针都使用相同的画布元素绘制。我创建了一个drawScene()
函数,它每秒运行一次并重绘整个时钟。如果你想更深入地看一下代码,我会将它发布到这篇文章底部链接的jsbin中。
目标是drawScene()
方法调用drawClockFace()
方法,该方法将当前秒/分钟/小时传递给根据传入时间绘制手的各个函数(即{{{ 1}})。
问题:
如何在不旋转整个画布的情况下旋转画布的各个组件(即我的时钟上的秒针)?我知道我需要根据当前秒数来计算从中心原点绘制线的位置。我只是不确定需要确定“在哪里”画线的宝石计算。
这是我到目前为止所做的 - 请注意它并不是超级干净,因为我一直在使用它。 http://codepen.io/tconroy/pen/BcEbf
答案 0 :(得分: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
}
}
现在只需将这些角度提供给渲染功能:
(function loop() {
renderClock()
requestAnimationFrame(loop);
})();
如果你想每秒更新替换rAF(你也可以从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, /// 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();
}
此辅助函数根据角度和三角函数计算终点:
function lineToAngle(x, y, length, angle) {
return {
x: x + length * Math.cos(angle),
y: y + length * Math.sin(angle)
}
}
渲染钟面的其他提示:
如果你正在制作一个时钟,那么这个提示可能非常有用 - 而不是每次更新清除和渲染面部,而是可以渲染面部一次,将画布转换为数据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() + ')';
然后在这里开始循环。
答案 1 :(得分:1)
你可以使用context.save&amp; context.restore暂时旋转部分时钟(如秒针)
这个想法是:
演示:http://jsfiddle.net/m1erickson/PjZz3/
示例代码:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" />
<script src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: white; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.strokeStyle="black"
ctx.fillStyle="ivory";
function animate() {
requestAnimFrame(animate);
// clear the canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// Draw the clock face
ctx.beginPath();
ctx.arc(150,150,50,0,Math.PI*2);
ctx.closePath();
ctx.lineWidth=3;
ctx.stroke();
ctx.fillStyle="ivory";
ctx.fill();
ctx.fillStyle="black";
ctx.fillText("12",145,115);
// Separately rotate the second hand
// using ctx.save and ctx.restore
// plus ctx.rotate
// calc the rotation angle
var d=new Date();
var radianAngle=(d.getSeconds()/60)*Math.PI*2;
ctx.save();
ctx.translate(150,150);
ctx.rotate(radianAngle);
ctx.moveTo(0,0);
ctx.lineTo(45,0);
ctx.stroke();
ctx.restore();
}
animate();
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=350 height=350></canvas>
</body>
</html>