複習Javascript到Canvas的知識點,看到一個使用Canvas繪製的靜態時鐘例子,便想將其變成動態顯示系統時間的時鐘動畫。另外再配上數位顯示的時鐘,一個小小的時鐘模組的誕生了!目前的介面還比較粗糙,只有簡單的介面和動畫效果。
這個時鐘包含兩個部分,動畫圓盤時鐘和數位時鐘。首先是使用超時呼叫setTimeout()方法做一個循環動畫的效果以顯示時間。先看數字時鐘的程式碼,比較簡單,圓盤時鐘的做法也是模仿數字時鐘做的。
var ntimeoutId = setTimeout(ntimeOut,0);function ntimeOut() { clearTimeout(ntimeoutId); var now = new Date(); var hours = now.getHours().toString(), minutes = now.getMinutes().toString(), seconds = now.getSeconds().toString(); var time = hours+" : "+minutes+" : "+seconds; var timep = document.getElementById("time"); timep.innerHTML = time; ntimeoutId = setTimeout(ntimeOut, 1000); }
1、首先是設定一個超時呼叫以首次呼叫方法來顯示系統目前時間,時間之所以設定為0 ,是為了沒有延遲地顯示時間。
2、每次循環之前清除前一次的超時呼叫(為什麼要這樣做,我還不清楚???可能是為了記憶體效能相關,註解了以後程式也能正常執行。
4、為了動態顯示在頁面中,在Html頁面中定義了一個空的p元素,以存放該時間字串。使用DOM操作將其新增至p元素即可動態顯示。
5、透過不停循環超時調用,就可以動態顯示數字時鐘了, 同過開發者工具也可以看到p元素裡的動態變化。
接下來就是製作圓盤時鐘動畫,圓盤和數值的刻畫都比較簡單,只要使用context.arc()方法和context .fillText()方法即可。以下是其原始碼:
context.beginPath(); context.restore(); context.translate(0,0); context.clearRect(0,0,300,300); //绘制时钟内外边框 context.arc(150,150,149,0,2 * Math.PI,false); context.moveTo(295,150); context.arc(150,150,145,0,2 * Math.PI,false); context.font = "bold 18px Arial"; context.textAlign = "center"; //绘制时钟表盘数值 context.fillText("12",150,25); context.fillText("3",285,150); context.fillText("6",150,290); context.fillText("9",15,150); context.fillText("1",215,45); context.fillText("2",265,95); context.fillText("4",265,225); context.fillText("7",95,275); context.fillText("5",215,275); context.fillText("8",35,225); context.fillText("10",35,95); context.fillText("11",75,45); context.stroke(); context.closePath();
接下來就是指標的繪製了,指標的繪製中參考高程中的做法,使用變換操作context.translate()方法改變原點,再繪製路徑成為指標會方便很多。另一個繪製指標的難度是弧度的計算,當然這就是數學問題了。下面先看原始碼:
//绘制指针 context.save(); context.translate(150,150); //时针 context.moveTo(0,0); hour(hours); function hour(thour) { context.save(); var newhour = 0; if(thour>12) { newhour = thour-12; } else { newhour = thour; } context.rotate((2*Math.PI/12)*newhour); context.lineTo(0,-80); context.restore(); } //分针 context.moveTo(0,0); minute(minutes); function minute(tminute) { context.save(); context.rotate((2*Math.PI/12)*tminute/5); context.lineTo(0,-110); context.restore(); } //秒针 context.moveTo(0,0); second(seconds); function second(tsecond) { context.save(); context.fillStyle = "#fff"; context.rotate((2*Math.PI/12)*tsecond/5); context.lineTo(0,-120); context.restore(); } context.stroke();
在繪製指標中,每個指標都使用了函數來改變每次指標繪製的弧度來實現指針轉動的動畫效果。對於時針,則將二十四小時制轉化為十二小時制,每次轉動30°即可。分針和秒針則是轉為0到11進行轉動,簡單的數學問題,相信大家都是比我厲害的,當時我還糾結了一陣子。在每個函式中都有使用context.save()方法和context.restore()方法,是為了儲存和復原初始化時的路徑,不然指標都要跑偏啦。
所有工作基本上準備就緒了,接下來只要將鐘盤和指針都放在超時調用中即可,但仍然有些問題需要注意的,比如說鐘盤和指針的原點設定不同,要注意使用儲存和復原還原初始化時候的路徑,不然鐘盤和指標都要跑偏了。另外要注意的是,使用Canvas製作動畫,每次的動畫循環都是要清空畫布重新繪製,不然指針一直轉轉,轉成一朵花的樣子。
完整的圓盤時鐘程式碼如下:
//显示指针时间var drawing = document.getElementById("drawing");if(drawing.getContext) { var context = drawing.getContext("2d"); var rtimeoutId = setTimeout(roudClock,0); function roudClock() { clearTimeout(rtimeoutId); context.beginPath(); context.restore(); context.translate(0,0); context.clearRect(0,0,300,300); //绘制时钟内外边框 context.arc(150,150,149,0,2 * Math.PI,false); context.moveTo(295,150); context.arc(150,150,145,0,2 * Math.PI,false); context.font = "bold 18px Arial"; context.textAlign = "center"; //绘制时钟表盘数值 context.fillText("12",150,25); context.fillText("3",285,150); context.fillText("6",150,290); context.fillText("9",15,150); context.fillText("1",215,45); context.fillText("2",265,95); context.fillText("4",265,225); context.fillText("7",95,275); context.fillText("5",215,275); context.fillText("8",35,225); context.fillText("10",35,95); context.fillText("11",75,45); context.stroke(); context.closePath(); var now = new Date(); var hours = now.getHours(), minutes = now.getMinutes(), seconds = now.getSeconds(); //绘制指针 context.save(); context.translate(150,150); //时针 context.moveTo(0,0); hour(hours); function hour(thour) { context.save(); var newhour = 0; if(thour>12) { newhour = thour-12; } else { newhour = thour; } context.rotate((2*Math.PI/12)*newhour); context.lineTo(0,-80); context.restore(); } //分针 context.moveTo(0,0); minute(minutes); function minute(tminute) { context.save(); context.rotate((2*Math.PI/12)*tminute/5); context.lineTo(0,-110); context.restore(); } //秒针 context.moveTo(0,0); second(seconds); function second(tsecond) { context.save(); context.fillStyle = "#fff"; context.rotate((2*Math.PI/12)*tsecond/5); context.lineTo(0,-120); context.restore(); } context.stroke(); context.restore(); context.translate(0,0); context.save(); rtimeoutId = setTimeout(roudClock,1000); } }
最後總結我在這次Demo的練習中遇到的幾點問題:
1、畫布重繪問題
#在指標動畫循環的時候,前一個路徑都沒有方法清除,造成每個循環都留下印記。嘗試了小範圍地使用clearRect()方法,結果發現只能在範圍內清除了錶盤和數字的內容,指針依然會留下痕跡。後來透過搜索,得到的答案是使用Canvas製作動畫一定是要進行重繪的,在新繪製內容前要清空畫布內容,每一次的動畫變化都要清空一次。重繪的方法可以參考一下鏈接,我使用的是clearRect()方法清空整個畫布。
2、save()方法和restore()方法的使用
因為我的鐘盤和指標的原點設定不同,所以進行重繪後,鐘盤的原點會改變為指標的原點,因此要利用save()方法和restore()方法改變。除了這個地方也有其他一些地方需要用到,適合操作以後改變了設定,但後續操作需要用原設定的情況。注意的是這兩個方法只會保存和恢復設定,而不是內容。
3、translate()方法的使用
translate()方法是屬於變換操作中的,可改變畫布的原點,預設畫布的原點在畫布的左上角。使用這個方法可以更輕易地繪製指標的路徑,當然還有一些其他需要使用的地方,我還沒有接觸到。
4、setTimeout()方法的使用
使用逾時呼叫是比間歇呼叫更好的方法,使用逾時呼叫可以模擬間歇調用。透過在超時呼叫傳入的函數中再添加超時呼叫就可以很好地模仿循環。也可以在函數中根據一些條件限定迴圈的次數和時間。
5、對應當前時間,弧度的使用方法
這就是數學問題,我在這個問題糾結了好一會兒,沒有拐過彎來,明白其中的原理就不是技術上的難題了。
6、對於時間重複取得和重複使用方法的問題
對於程式碼中還有重複程式碼的情況,我還沒有想到更好的方法減少冗餘。例如在獲取時間上,在兩個超時呼叫中均有重複定義,但若是把他們放在全域中,則沒有了動畫效果,只是顯示載入完成後的那個靜態時間。另外的是指標的函數設定中有重複的部分,是否可以合為一個函數方法再進行呼叫呢。
歡迎大家提出想法,對於不足的地方提出建議,一起來交流。
以上是使用Canvas製作時鐘動畫的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!