前言
JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks、No Compilers、No Libraries、No Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
本日目標
透過 JavaScript 的 Date 物件分別取得"時"、“分”、"秒"並計算出在圓上的相對應角度,最後搭配 CSS 的 transform
和 transition
屬性,製作出一個簡易的時鐘。
解析程式碼
HTML 部分
由最外層的"clock"部分包住內層的"clock-face"和其內部的"時針"、“分針”、“秒針”,形成一個完整的巢狀結構。
1 2 3 4 5 6 7
| <div class="clock"> <div class="clock-face"> <div class="hand hour-hand"></div> <div class="hand min-hand"></div> <div class="hand second-hand"></div> </div> </div>
|
CSS 部分
首先,將物件 transform
的基準點,更改為最右端。接著,將所有指針都預先固定在12點鐘方向。最後,透過 transition
屬性還有 transition-timing-function
屬性,分別調整 CSS animation 效果變化速度和做出指針移動時的彈跳效果。
1 2 3 4 5 6 7 8 9 10 11 12 13
| .hand { width: 50%; height: 6px; background: black; position: absolute; top: 50%; transform-origin: 100%; transform: rotate(90deg); transition: all 0.05s; transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1); }
|
補充說明1:
transform
預設是以物件中心作為平移、旋轉、縮放、傾斜時的基準點。詳細內容見此
補充說明2:
transition timing function
,可用來定義轉場發生的時間曲線,以四個參數的貝茲曲線代表。詳細內容見此
JS 部分
首先,分別取得代表"時針"、“分針”、"秒針"的標籤。
1 2 3 4
| const secondHand = document.querySelector('.second-hand'); const minsHand = document.querySelector('.min-hand') const hourHand = document.querySelector('.hour-hand')
|
建立 Date
物件,取得"時"、“分”、"秒"的資料,以此算出所應旋轉的角度(注意,角度必須加上早先設定的90度,才會是正確的),之後分別調整 CSS 的 transform
屬性。
最後用 setInterval()
方法,設定每1000毫秒(1秒)就執行 setDate()
方法一次,藉此動態改變 rotate
的值。
(時針、分針、秒針的原理都一樣,只是在角度計算上有所差異)
1 2 3 4 5 6 7 8 9 10 11 12
| function setDate(){ const now = new Date();
const seconds = now.getSeconds(); const secondsDegrees = ((seconds/60)*360) + 90; secondHand.style.transform = `rotate(${secondsDegrees}deg)`; }
setInterval(setDate,1000)
|
分別設定完"時針"、“分針”、"秒針"後,setDate()
方法如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function setDate(){ const now = new Date();
const seconds = now.getSeconds() const secondsDegrees = ((seconds/60)*360) + 90; secondHand.style.transform = `rotate(${secondsDegrees}deg)`; const mins = now.getMinutes(); const minsDegrees = ((mins/60)*360) + 90; minsHand.style.transform = `rotate(${minsDegrees}deg)`;
const hours = now.getHours(); const hoursDegrees = ((hours/12)*360) + 90; hourHand.style.transform = `rotate(${hoursDegrees}deg)`; }
|
以上都完成後,一個簡單的時鐘就出現了。但仔細一看就會發現指針在某個時間點會突然倒轉一圈。
舉"秒針"為例,在59秒~0秒之間,數值上的角度會從444度變為90度(分針也是如此),整整倒轉354度接近一圈,這就解釋了為什麼指針會有突然倒轉的現象。
而我們可以分別記錄時針和分針所走的圈數,並將原來計算出的度數加上360度*圈數,解決指針倒轉的問題。
用 if 判斷到 0 秒(分)時,就將圈數加1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| var secRound = 0; var minRound = 0;
function setDate(){ const now = new Date();
const seconds = now.getSeconds() if(seconds == 0){ secRound += 1; } const secondsDegrees = ((seconds/60)*360) + 360*secRound + 90; secondHand.style.transform = `rotate(${secondsDegrees}deg)`; const mins = now.getMinutes(); if(mins == 0){ minRound += 1; } const minsDegrees = ((mins/60)*360) + 360*minRound + 90; minsHand.style.transform = `rotate(${minsDegrees}deg)`;
const hours = now.getHours(); const hoursDegrees = ((hours/12)*360) + 90; hourHand.style.transform = `rotate(${hoursDegrees}deg)`; }
|
最後的最後,我們還可以讓指針的位置更加精準。一般而言,秒針每走一格,分針應該跟著移動一點,同理時針也是如此。
對分針而言移動每過一分鐘移動6度,我們可以用 (秒數/60)*6,算出實際上每過一秒鐘,分針應該要跟著移動多少度。
對時針而言移動每過一小時移動30度,我們可以用 (分鐘數/60)*30,算出實際上每過一分鐘,時針應該要跟著移動多少度。
1 2 3 4 5 6 7 8 9 10
| const mins = now.getMinutes(); if(mins == 0){ minRound += 1; } const minsDegrees = ((mins/60)*360) + 360*minRound + ((seconds/60)*6) + 90; minsHand.style.transform = `rotate(${minsDegrees}deg)`;
const hours = now.getHours(); const hoursDegrees = ((hours/12)*360) + ((mins/60)*30) + 90; hourHand.style.transform = `rotate(${hoursDegrees}deg)`;
|
補充說明1:
rotate(${hoursDegrees}deg)
是 ES6 Template literals
的寫法。詳細介紹 Template literals