使用canvas繪制時鐘
準備工作
在HTML中指定一個區域放置時鐘:
<div id="clock" style="position: relative;"></div>
時鐘的一些外觀設定:
var width = 260; // 桌布寬度
var height= 260; // 桌布高度
var dot = {
x : width / 2,
y : height / 2,
radius : 6
}; // 圓點位置、半徑
var radius = 120; // 圓半徑
var borderWidth = 6; // 圓邊框寬度 創建<canvas>元素:
var clock = document.getElementById('clock');
var clockBg = document.createElement('canvas');
var clockPointers = document.createElement('canvas');
clockPointers.width = clockBg.width = width;
clockPointers.height = clockBg.height = height;
clockPointers.style.position = 'absolute';
clockPointers.style.left = 0;
clockPointers.style.right = 0;
clock.appendChild(clockBg);
clock.appendChild(clockPointers);</pre>
這里要創建兩個<canvas>元素,目的在于把時鐘的圓盤跟指針分離開。這是因為指針要根據當前時間擦除重繪,如果放置在一個<canvas>中,擦除的時候就會把圓盤也給擦掉了。
繪制圓盤
但凡要在<canvas>中繪圖,都要先獲得其上下文,對應的接口是 canvas.getContext :
var bgCtx = clockBg.getContext('2d'); 目前canvas.getContext接口的唯一一個合法參數是' 2d ',將來應該會支持3D繪圖。
先來繪制最外面的圓框:
bgCtx.beginPath();
bgCtx.lineWidth = borderWidth;
bgCtx.strokeStyle = '#000';
bgCtx.arc(dot.x, dot.y, radius, 0, 2 * Math.PI, true);
bgCtx.stroke();
bgCtx.closePath();
繪圖的流程其實都是類似的:
- 調用 context.beginPath() 新建路徑;
- 設置顏色等樣式;
- 調用路徑函數生成路徑;
- 畫線( stroke )或者填充( fill );
- 調用 context.closePath() 關閉路徑;
</ol>
上面用到的 context.arc 接口可以生成圓弧路徑,其詳細說明參見 此處 。
用類似的方法,畫出圓點:
bgCtx.beginPath();
bgCtx.fillStyle = '#000';
bgCtx.arc(dot.x, dot.y, dot.radius, 0, 2 * Math.PI, true);
bgCtx.fill();
bgCtx.closePath();
此時,結果如下圖所示:
繪制刻度
最復雜的地方就是畫刻度了,這里要先復習一下數學中的 三角函數 :
刻度的起始位置就是圓框上的一個點,第一步就是要知道這個點的坐標。上圖中:
sinθ = AC / AO
cosθ = OC / AO
其中 AO即為圓半徑 ,而θ的值則根據刻度而定。0是π/2,3是0,6是3π/2,9是π:
由此可得到刻度起始點的位置為:
x = 圓點橫坐標 + AO * cosθ
y = 圓點縱坐標 + AO * sinθ
同理可算出刻度結束點的位置為(結束點相當于在一個半徑為 圓框半徑-刻度長度 的圓上):
x = 圓點橫坐標 + (AO - 刻度長度) * cosθ
y = 圓點縱坐標 + (AO - 刻度長度) * sinθ
于是,這程序可以寫了:
for (var i = 0, angle = 0, tmp, len; i < 60; i++) {
bgCtx.beginPath();
// 突出顯示能被5除盡的刻度
if (0 === i % 5) {
bgCtx.lineWidth = 5;
len = 12;
bgCtx.strokeStyle = '#000';
} else {
bgCtx.lineWidth = 2;
len = 6;
bgCtx.strokeStyle = '#999';
}
tmp = radius - borderWidth / 2; // 因為圓有邊框,所以要減去邊框寬度
bgCtx.moveTo(
dot.x + tmp * Math.cos(angle),
dot.y + tmp * Math.sin(angle)
);
tmp -= len;
bgCtx.lineTo(dot.x + tmp * Math.cos(angle), dot.y + tmp * Math.sin(angle));
bgCtx.stroke();
bgCtx.closePath();
angle += Math.PI / 30; // 每次遞增1/30π
}</pre>
畫好刻度后,結果應該是這樣:
畫指針
先得獲取指針<canvas>的上下文:
var ptxContext = clockPointers.getContext('2d'); 由于畫指針的操作每隔一秒都要執行一次,所以這里就寫成一個函數,方便傳給setInterval調用:
function updatePointers() {
ptCtx.clearRect(0, 0, width, height); // 清掉原來的指針
// 獲取當前時間
var now = new Date();
var h = now.getHours();
var m = now.getMinutes();
var s = now.getSeconds();
// 算出時分秒指針現在應指向圓的幾分之幾處
h = h > 12 ? h - 12 : h;
h = h + m / 60;
h = h / 12;
m = m / 60;
s = s / 60;
drawPointers(s, 2, 92); // 畫秒針
drawPointers(m, 4, 82); // 畫分針
drawPointers(h, 6, 65); // 畫時針
}</pre>
drawPointers函數的實現是:
// angle是角度,lineWidth是指針寬度,length是指針長度
function drawPointers(angle, lineWidth, length) {
angle = angle Math.PI 2 - Math.PI / 2;
ptCtx.beginPath();
ptCtx.lineWidth = lineWidth;
ptCtx.strokeStyle = "#000";
ptCtx.moveTo(dot.x, dot.y);
ptCtx.lineTo(dot.x + length * Math.cos(angle), dot.y + length * Math.sin(angle));
ptCtx.stroke();
ptCtx.closePath();
}</pre>
這里主要也是用到三角函數,就不啰嗦了,但是要注意angel的計算。由于傳入的angel是一個百分數,所以要乘以一個圓周,也就是2π,才知道對應的弧度,算出來以后還要減去π/2,因為從上面的坐標圖就可以看到,0是位于x軸而不是y軸除,剛好比正常的時鐘多了π/2。
最后別忘了調用updatePointers實時更新指針:
setInterval(updatePointers, 1000);
updatePointers();
這下時鐘完全出來了,除了初步熟悉<canvas>繪圖API外,還順便復習了一次三角函數。
</div>
原文 http://www.heeroluo.net/article/detail/95/draw-clock-in-canvas