iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 29
0
Modern Web

新手也能懂的JS30系列 第 29

JS30-Day29-Countdown Timer

Day29-課題內容

在 JS30第二天的 JS & CSS Clock 中,我們利用Javascript中的 setInterval() 方法以及CSS屬性,在頁面上做出一個會轉動的虛擬時鐘。而在倒數第二天的課題當中,我們要做出一個倒數計時器。[1]
實作連結

進入課題

在倒數計時器的頁面中我們包含了幾種功能:

  1. 從指定的時間倒數計時至0秒
  2. 顯示倒數計時完成的時間

在作者的草稿中,我們看到頁面上分成了 <button><form> 兩種輸入形式,而首先我們來處理 <button> 的部分。

<div class="timer">
    <div class="timer__controls">
        <button data-time="20" class="timer__button">20 Secs</button>
        <button data-time="300" class="timer__button">Work 5</button>
        <button data-time="900" class="timer__button">Quick 15</button>
        <button data-time="1200" class="timer__button">Snack 20</button>
        <button data-time="3600" class="timer__button">Lunch Break</button>
        <form name="customForm" id="custom">
            <input type="text" name="minutes" placeholder="Enter Minutes">
        </form>
    </div>
    <div class="display">
      <h1 class="display__time-left"></h1>
      <p class="display__end-time"></p>
    </div>
</div>

將我們需要的 DOM 元素選取起來之後,先將全部的 <button> 元素加上監聽事件 onclick ,並指定觸發時執行的函示:

const buttons = document.querySelectorAll('.timer__button');
const timeLeft = document.querySelector('.display__time-left');
const endTime = document.querySelector('.display__end-time');
const input = document.querySelector('form');

buttons.forEach(function (button) {
    button.addEventListener('click', setTime);
});

而在執行的函示中我們需要完成幾件事情:

  1. 取得倒數的時間長度
  2. 取得目前的時間
  3. 執行倒數計時
  4. 顯示剩餘的時間
  5. 顯示結束的時間

因此在觸發的函式當中,我們先取得倒數計時的時間長度與目前的時間,再透過指定參數給其他函式,完成後面三個功能,我們的程式碼的可讀性會比較高。
除此之外,因為會使用到 setInterval() 方法,我們先在全域環境下指定一個代表變數setInterval() ,方便我們後續使用 clearInterval() 方法,並且在執行的函式中先執行一次 clearInterval() 將先前的 setInterval() 方法先清除掉,避免 Bug 發生:

let count;

function setTime(event) {
    //清除之前的 setInterval() 方法
    clearInterval(count);
    //取得按鍵中 data-attibute 中所代表的倒數計時時間並轉為數字
    let remainTime = parseInt(this.dataset.time);
    //取得目前的時間
    let currentTime = new Date();
    //執行顯示剩餘時間的函式
    showRemainTime(remainTime);
    //執行倒數計時的函式
    countDown(remainTime);
    //執行顯示結束時間的函式
    showEndTime(currentTime, remainTime);
};

在顯示剩餘時間在頁面上的函式當中,我們要將時間轉換成我們想要的格式,並將這個字串內容指定為 <.display__time-left> 元素的 innerHTML 內容。
可以看到我們將分別代表時、分、秒的參數加上一判斷式,當該值小於零,我們就將其值修改為兩個數字的型態,如此一來就能讓顯示的字串一直維持類似電子錶的格式:

function showRemainTime(time) {
    //利用 parseInt 取得小時的整數
    let hour = parseInt(time / 3600);
    if (hour < 10) {
        hour = `0${hour}`;
    }
    //利用 parseInt 取得分鐘的整數
    let min = parseInt((time - hour * 3600) / 60);
    if (min < 10) {
        min = `0${min}`;
    }
    //利用 % 取得秒數
    let sec = time % 60;
    if (sec < 10) {
        sec = `0${sec}`;
    }

    let text = `${hour}:${min}:${sec}`;
    //指定顯示剩餘時間元素的 innerHTML 
    timeLeft.innerHTML = text;
    //指定頁面標題 
    document.title = text;
};

在執行倒數計時的函式中,我們要透過 setInterval() 方法,每秒將倒數剩餘時間減去一秒,並且將新的剩餘時間,重新印到頁面上。除此之外當剩餘為0時,我們要移除該 setInterval() 方法,因此在此執行函式當中,我們要透過判斷式來幫助我們執行:

function countDown(remainTime) {
    //將變數 count 指定為 每秒執行一次的 setInterval() 函式
    count = setInterval(function () {
        //當剩餘時間 > 0,將剩餘時間減去一秒,並重新顯示在頁面上
        if (remainTime > 0) {
            remainTime--;
            showRemainTime(remainTime)
        //當剩餘時間 =< 0,清除 setInterval() 函式
        } else {
            clearInterval(count);
        }
    }, 1000)
};

最後是顯示結束時間的函式。在此函式當中,我們已經利用參數,取得剛開始觸發事件時,倒數計時總時間與當下的時間,而我只要將當下的時間轉為秒數,再透過換算成小時、分鐘、秒,最後再指派給 <.display__end-time> 元素的 innerHTML 內容:

function showEndTime(currentTime, remainTime) {
    //取得目前時間的時、分、秒
    let currentHour = currentTime.getHours();
    let currentMin = currentTime.getMinutes();
    let currentSec = currentTime.getSeconds();
    //結束時間為目前的時間加上倒數時間總長度
    let predictTime = remainTime + currentHour * 3600 + currentMin * 60 + currentSec;
    //換算結束時間的小時
    let predictHour = parseInt(predictTime / 3600);
    if (predictHour < 10) {
        predictHour = `0${predictHour}`;
    }
    //換算結束時間的分鐘
    let predictMin = parseInt((predictTime - predictHour * 3600) / 60);
    if (predictMin < 10) {
        predictMin = `0${predictMin}`;
    }
    //換算結束時間的秒鐘
    let predictSec = predictTime % 60;
    if (predictSec < 10) {
        predictSec = `0${predictSec}`;
    }
    //指定顯示結束時間元素的 innerHTML 
    endTime.innerHTML = `${predictHour % 24}:${predictMin}:${predictSec}`;
};

基本的功能我們已經完成,再來只要將 <form> 元素中的 <input> 數值指派給倒數計時功能即可完成,而為了避免輸入的字不是數字,我們在判斷是中加上 isNaN() 方法來幫助我們判斷是否為數字。
而在之前的章節中有提過,<form> 元素觸發 onsubmit 事件時,頁面會被重整,因此需要先加上 event.preventDefault() 方法來避免:

input.addEventListener('submit', setInputTime);

function setInputTime(event) {
    event.preventDefault();
    //取得 form 容器中的 input 子元素嶿入的資料
    let inputValue = this.querySelector('input').value;
    if (isNaN(inputValue)) {
        window.alert('請輸入數字!!')
    } else {
        clearInterval(count);
        let remainTime = inputValue * 60;
        let currentTime = new Date();
        countDown(remainTime);
        showRemainTime(remainTime);
        showEndTime(currentTime, remainTime);
    }
    this.reset();
};

完成以上的程式碼之後,就能完成今天的課題囉!!

總結

在倒數第二天的內容當中,我們重新複習了 setInterval() 方法與 new Date () 物件,並將他們組合起來做出倒數計時器。透過這兩種技能,相信我們能做出更多跟時間有關的有趣小工具!

參考資料

  1. javascript30

上一篇
JS30-Day28-Video Speed Controller
下一篇
JS30-Day30-打地鼠
系列文
新手也能懂的JS3030

尚未有邦友留言

立即登入留言