iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0

Countdown Clock 倒數計時器

提供使用者直接輸入想要倒數的分鐘數,另外也提供快捷鍵的倒數選項。
一邊告訴你剩下多少時間,底下也顯示幾點幾分時間到。

https://ithelp.ithome.com.tw/upload/images/20241008/20169174n2CeEegkqW.png

個人codepen

技巧點

1. new Date()相關的操作方法

直接從程式碼紀錄操作過程

 <button data-time="20" class="timer__button">20 Secs</button>

const controls = document.querySelectorAll(".timer__button");
     
// 將所有的快捷按鈕綁定監聽,並從自訂義的屬性dataset中取出秒數資料。此秒數為字串,所以用parseInt將其轉換為數字,再做處理。
controls.forEach(button => {
  button.addEventListener('click', function() {
    const seconds = parseInt(this.dataset.time);
    startTimer(seconds);
  });
});
// 設定定時器變數
let timer;

function startTimer(seconds) {
  // 一進入函式要先清除定時器,因為使用者可能反覆去點擊不同快捷按鈕,會觸發多次計時器。
  clearInterval(timer);
  
  // 計算結束的時間,那因為Date.now()的數值會是毫秒數,一秒等於1000毫秒,先轉換成毫秒,再相加,就可以知道接下來要結束的時間。再將她處理成文字,渲染在畫面。
  const endTime = Date.now() + seconds * 1000;
  updateEndTime(endTime);
  
  // 處理每秒倒數顯示的時間,要先處理一次,不然使用者設定倒數時間送出後,第一時間看不到倒數的秒數。等下次看到已經過了一秒。
  displayTime(seconds);
  
  // 設定間隔計時器,每隔一秒就計算一次,目前的時間和結束的時間之差距。
  timer = setInterval(() => {
  
    // 結束的時間是固定的,但現在的時間會每秒變動。相差的值因為是毫秒數,要先除於1000轉換為秒數,因為數字不會剛好,所要再用Math.round四捨五入。
    const remainingSeconds = Math.round((endTime - Date.now()) / 1000);
    
    // 秒數如果變成負值的時候,代表計時器倒數結束,要清除定時器,並且終止函式繼續向下運行。
    if (remainingSeconds < 0) {
      window.alert("time up");
      clearInterval(timer);
      return;
    }
    
    // 倒數秒數還是正值的情況,就要繼續更新畫面中的倒數數字。
    displayTime(remainingSeconds);
  }, 1000);
}

處理畫面中的倒數數字更新

function displayTime(seconds) {
  // 秒數除於60可以知道幾分鐘,那因為會有小數點,小數點代表不到一分鐘,所以直接Math.floor無條件捨去。
  const minutes = Math.floor(seconds / 60);
  
  // 一分鐘為60秒,所以將秒數跟60取餘數,剩下的就會是秒數了。
  const remainderSeconds = seconds % 60;
  
  // 處理畫面中的倒數數字的更新
  countDownTxt.textContent = `${String(minutes).padStart(2, '0')} : ${String(remainderSeconds).padStart(2, '0')}`;
}

處理畫面中預計結束時間的文字

function updateEndTime(timestamp) {
  // 傳過來的是毫秒數,放入到new Date可以得到現在的時間。
  const end = new Date(timestamp);
  
  // 取時間的數字
  const hours = end.getHours();
  
  // 取分鐘的數字
  const minutes = end.getMinutes();
  
  // 因為小時會是24小時制,所以要額外判斷是否超過12點
  const newHours = hours > 12 ? hours - 12 : hours;
  const amPm = hours >= 12 ? '下午' : '上午';
  
  // 將時間的數字,只有一位數時,就補0上去,這樣比較美觀
  endTimeTxt.textContent = `Be Back at:${amPm} ${String(newHours).padStart(2, '0')} : ${String(minutes).padStart(2, '0')}`;
}

2. padStart(targetLength, padString)

會從字串的左側開始添加指定字元,一直重複添加到指定的長度。
targetLength為填充後的字元長度,padString為想要添加的字元。padString預設是空白。

// 字串原本長度為2,需要添加字元到長度為5為止,因為padString沒設定,預設就是填充空白插入。
"IT".padStart(5); // "   IT"

"IT".padStart(10, "what is "); // "'what is IT'"

// 填充123456到abc中,但因為只需要填充到6的長度,所以後面456就沒用到了。
"abc".padStart(6, "123456"); // "123abc"

倒數的秒數只有一個位數時,會變成10:9的方式出現,為了美觀要把它變成10:09,因此需要額外處理秒數。

3.parseInt(string, radix)

  • parseInt() 將字串轉換為以十進位表示的整數。
  • string為要轉換的字串,如果不是字串的話,會先用toString()先轉換為字串,才解析。
  • radix為進位系統。

parseInt會忽略前後空白,並根據radix進位系統去解析,當遇到無法解析的字元,會忽略該字元及其後的所有字元,並停止繼續解析剩餘的文字,然後回傳目前為止的結果。

parseInt("   123",10) // 123 忽略前面空白
parseInt("") // 無法解析返回NaN
parseInt("16px",10) // 16  後面的英文字母無法解析成數字,所以只返回前面的16
parseInt(" 777   ",10) //777,前面空白忽略,直接解析777,後面遇到空白也會停止解析

心得

主要是時間相關方法的熟練度,要轉來轉去的,轉到後面我都暈了。

表單的form預設submit行為記得要preventDefault。另外輸入欄位可能會出現非數字的行為,要先做判斷處理,用parseInt()將字串轉成數字,遇到不能轉成數字的話,會出現NaN的狀況。或是數字會有負數。就可以擋掉,並給使用者提示,請他好好輸入!不要害我畫面出現NaN...


上一篇
Video Speed Controller UI
系列文
鱷魚帶我練習JavaScript之個人練功坊29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言