iT邦幫忙

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

JS30 錄系列 第 29

Day 29 - Final Countdown

任務目標

今天要來寫個倒數計時器,這也象徵著鐵人賽的倒數。

作法

HTML 架構如下:

<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>

該怎麼實現倒數的邏輯呢?先看以下程式碼。

const buttons = document.querySelectorAll('[data-time]')

function startTimer() {
	const seconds = parseInt(this.dataset.time);
	// 開始計時就是把預設秒數作為參數傳進計時器中
	timer(seconds);
}

// 每個按鈕被按時,會開始計時
buttons.forEach(button => button.addEventListener('click', startTimer));

要倒數計時,首先要設定好碼錶。所以要讓「要倒數多久」做為參數傳入計時器。 HTML 裡面那些倒數計時設定鈕的 data-time 屬性中記錄著這些數字,當我們按按鈕時,就會觸發事件將時間傳進計時器。

自訂計時時間也是,把寫進 <input> 裡面的值轉換為秒數,傳進 timer() 計時器內。要存取表單的 DOM 來增加監聽器,若覺得 querySelector 太繁瑣,可以直接利用 document.[表格的 name] 來存取該表格。

document.customForm.addEventListener('submit', function(e) {
	e.preventDefault();
	const mins = this.minutes.value;
	timer(mins * 60);
	this.reset();
})

接下來就是計時器的核心:

function timer(sec) {
	clearInterval(countdown)

  // 現在時間與計時終了的時間
	const now = Date.now();
	const then = now + sec * 1000;

  // 計時開始前顯示出初始設定
	displayTimeLeft(sec);
	displayEndTime(then);
	// 開始計時
	countdown = setInterval(() => {
		const secLeft = Math.round((then - Date.now()) / 1000);

		// 時間到就清除計時器並返回
		if(secLeft < 0) {
			clearInterval(countdown);
			return;
		}
		// 還沒就顯示剩餘秒數
		displayTimeLeft(secLeft);
		
	}, 1000);
}

運作的邏輯像這樣:

  1. 首先抓取現在的時間。
  2. 接著加上要倒數計時的秒數,得到結束後的時間
  3. 在開始計時前,先讓「倒數計時秒數」、「計時終了時間」兩個關鍵資訊顯示在畫面上
  4. 利用 setInterval 開始計時
  5. setInterval 會每秒中呼叫一次現在的正確時間,並且將其與最終時間相減,來算出還剩下多少秒
  6. 每秒鐘,如果時間到了,我們就會把 setInterval 這個計時器取消掉,然後返回
  7. 每秒鐘,如果時間還沒到,就會讓畫面顯示還剩多少秒

因為 setInterval 計時器不砍掉的話,會一直存在,若直接開啟下次計時,會變成有重複的計時器在進行動作,因此才這樣設計。

至於如何顯示時間,就跟 Day 2 邏輯差不多。程式碼如下:

// 用分 / 秒顯示剩下多久
function displayTimeLeft(sec) {
	const minutes = Math.floor(sec / 60);
	const remainderSecs = sec % 60;
	const display = `${minutes}:${remainderSecs < 10 ? '0' : ''}${remainderSecs}`;

	document.title = display;
	timerDisplay.textContent = display;
}

// 用 時 / 分 / 秒 顯示何時計時終了
function displayEndTime(timestamp) {
	const end = new Date(timestamp);
	const hour = end.getHours();
	const minutes = end.getMinutes();
	endTime.textContent = `Be Back At ${hour > 12 ? hour - 12 : hour}:${minutes < 10 ? '0' : ''}${minutes}`;
}

以上就是 JS30 第二十九篇!

Reference

Document.form
完整程式碼


上一篇
Day 28 - Video Speed Controller
下一篇
Day 30 - Whack A Mole !
系列文
JS30 錄30

尚未有邦友留言

立即登入留言