iT邦幫忙

2021 iThome 鐵人賽

DAY 2
0
自我挑戰組

JS30 學習日記系列 第 2

Day 2 - CSS + JS Clock

前言

JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No FrameworksNo CompilersNo LibrariesNo Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。

另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。


本日目標

透過 JavaScript 的 Date 物件分別取得"時"、"分"、"秒"並計算出在圓上的相對應角度,最後搭配 CSS 的 transformtransition 屬性,製作出一個簡易的時鐘。


解析程式碼

HTML 部分

由最外層的"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>

CSS 部分

首先,將物件 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);
}
補充說明1:

transform 預設是以物件中心作為平移、旋轉、縮放、傾斜時的基準點。詳細內容見此

補充說明2:

transition timing function,可用來定義轉場發生的時間曲線,以四個參數的貝茲曲線代表。詳細內容見此

JS 部分

首先,分別取得代表"時針"、"分針"、"秒針"的標籤。

/*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)`;
補充說明1:

rotate(${hoursDegrees}deg)ES6 Template literals 的寫法。
詳細介紹 Template literals

範例網頁請按此


上一篇
Day 1 - JavaScript Drum Kit
下一篇
Day 3 - Playing with CSS
系列文
JS30 學習日記30

尚未有邦友留言

立即登入留言