使用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
![]()