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
屬性,製作出一個簡易的時鐘。
由最外層的"clock"部分包住內層的"clock-face"和其內部的"時針"、"分針"、"秒針",形成一個完整的巢狀結構。
<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>
首先,將物件 transform
的基準點,更改為最右端。接著,將所有指針都預先固定在12點鐘方向。最後,透過 transition
屬性還有 transition-timing-function
屬性,分別調整 CSS animation 效果變化速度和做出指針移動時的彈跳效果。
.hand {
width: 50%;
height: 6px;
background: black;
position: absolute;
top: 50%;
/*以下是影片中教學的部分*/
transform-origin: 100%; /*改變 transform 的 x-axis*/
transform: rotate(90deg); /*初始位置從12點鐘出發*/
transition: all 0.05s; /*調整 CSS animation 變動的速度*/
transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1);
}
transform
預設是以物件中心作為平移、旋轉、縮放、傾斜時的基準點。詳細內容見此
transition timing function
,可用來定義轉場發生的時間曲線,以四個參數的貝茲曲線代表。詳細內容見此
首先,分別取得代表"時針"、"分針"、"秒針"的標籤。
/*JS*/
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
的值。
(時針、分針、秒針的原理都一樣,只是在角度計算上有所差異)
/*JS*/
function setDate(){
const now = new Date();
/*時針、分針、秒針的原理都一樣*/
const seconds = now.getSeconds();
const secondsDegrees = ((seconds/60)*360) + 90;/*旋轉的角度要加上預設的90度*/
secondHand.style.transform = `rotate(${secondsDegrees}deg)`;
}
setInterval(setDate,1000)
分別設定完"時針"、"分針"、"秒針"後,setDate()
方法如下。
/*JS*/
function setDate(){
const now = new Date();
/*時針、分針、秒針的原理都一樣*/
const seconds = now.getSeconds()
const secondsDegrees = ((seconds/60)*360) + 90;/*旋轉的角度要加上預設的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。
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;/*旋轉的角度要加上預設的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,算出實際上每過一分鐘,時針應該要跟著移動多少度。
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)`;
rotate(${hoursDegrees}deg)
是 ES6 Template literals
的寫法。
詳細介紹 Template literals