--- tags: 我的大魔王們 --- # 倒數計時( setTimeout & setInterval ) >參考文件: >[談談 JavaScript 的 setTimeout 與 setInterval](https://kuro.tw/posts/2019/02/23/%E8%AB%87%E8%AB%87-JavaScript-%E7%9A%84-setTimeout-%E8%88%87-setInterval/) >[JavaScript 中的倒數計時器](https://www.delftstack.com/zh-tw/howto/javascript/count-down-timer-in-javascript/#%E6%8E%A5%E4%B8%8B%E4%BE%86-%E6%88%91%E5%80%91%E5%91%BC%E5%8F%AB-startcountdown-duration-element-%E5%87%BD%E5%BC%8F-%E4%BB%A5%E7%A7%92%E7%82%BA%E5%96%AE%E4%BD%8D%E5%82%B3%E9%81%9E-duration-%E4%BB%A5%E5%8F%8A-display-%E5%85%83%E7%B4%A0%E7%9A%84-html-%E7%AF%80%E9%BB%9E-%E6%88%91%E5%80%91%E7%8F%BE%E5%9C%A8%E9%A1%AF%E7%A4%BA%E5%80%BC-01-30-%E5%9B%A0%E6%AD%A4-%E6%88%91%E5%80%91%E9%9C%80%E8%A6%81%E8%A8%88%E6%99%82%E5%99%A8%E5%BE%9E-01-29-%E9%96%8B%E5%A7%8B-%E9%80%99%E5%B0%B1%E6%98%AF%E7%82%BA%E4%BB%80%E9%BA%BC%E6%88%91%E5%80%91%E4%BD%BF%E7%94%A8%E6%8C%81%E7%BA%8C%E6%99%82%E9%96%93%E7%82%BA-duration-%E7%9A%84%E6%B8%9B%E4%B8%80%E5%85%83%E9%81%8B%E7%AE%97%E5%AD%90%E7%9A%84%E5%8E%9F%E5%9B%A0) >javascript info - [Scheduling: setTimeout and setInterval ](https://javascript.info/settimeout-setinterval) > ## setTimeout 與 setInterval的差別 :::info ![](https://i.imgur.com/3Y2Plhu.png) ::: <span style="margin-left:15px;">:star2:單位為**毫秒**</span> ### setTimeout + [setTimeout](https://developer.mozilla.org/en-US/docs/web/api/settimeout):延遲了某段時間之後,才去執行<span style="color:red;">「一次」</span>指定的程式碼,並且會執行一個函數或指定的一段代碼。 p.s. 當想要停止 setTimeout 的話就需要另外設定 [clearTimeout](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout)。 `比較特別的是 clearTimeout() 只會在 setTimeout() 指定的時間未到時才會有效果, 若 setTimeout() 已經被執行,那就沒用處了。` >javascript info - [setTimeout](https://javascript.info/settimeout-setinterval#settimeout) :::spoiler 語法: ```javascript let timerId = setTimeout(func|code, delay, arg1, arg2, ...) // func|code:當時間一到,執行一次程式。 // delay(可選的):延遲時間。如果未指定,則默認為 0。 // arg(可選的):一旦計時器到期 ,將傳遞給由 func指定的函數的附加參數。 ``` ::: ```javascript= let timeOut = window.setTimeout(( () => console.log("Hello!") ), 1000); // 停止 timeOut window.clearTimeout(timeOut); ``` :::spoiler function放外面寫法 ```javascript= // 如果把 function 放在外面,在 setTimeout 呼叫時,不需要加「 () 」 let timeout = setTimeout(callFun , 2000) function callFun() { console.log('觸發') } ``` [六角課程](https://courses.hexschool.com/courses/202011122/lectures/39771159) ::: ### setInterval + [setInterval](https://developer.mozilla.org/en-US/docs/Web/API/setInterval):延遲了某段時間之後,<span style="color:red;">「重複」</span>執行函數或指定的一段代碼,且每次執行之間有固定的時間延遲。 p.s. 當想要停止 setInterval 的話就需要另外設定 [clearInterval](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval)。 >javascript info - [setInterval](https://javascript.info/settimeout-setinterval#setinterval) :::spoiler 語法: ```javascript let timerId = setInterval(func|code, delay, arg1, arg2, ...) // func|code:當時間一到,重複執行程式,並每次間隔delay時間。 // delay(可選的):延遲時間。如果未指定,則默認為 0。 // arg(可選的):一旦計時器到期 ,將傳遞給由 func指定的函數的附加參數。 ``` ::: ```javascript= let interval = window.setInterval(( () => console.log("Hello!") ), 1000); // 停止 interval window.clearInterval(interval); ``` >兩者的不同之處是 **setTimeout()** 只會執行==一次==就結束, >而 **setInterval()** 則是會在間隔固定的時間==不斷重複==。 > > ## setTimeout - IIFE(立即呼叫函式) 題目:在五秒鐘之內,每秒鐘依序透過 console.log() 印出: 0 1 2 3 4 :::spoiler 寫法: ```javascript= // 傳統寫法: for(let i = 1 ; i<=5 ; i++){ (function(n){ // IIFE 立即呼叫函式 window.setTimeout(()=>{ console.log(i) },1000 * n) // 1000 * n = 1000*1、1000*2、1000*3... })(i) // 將i帶入參數 ⤻ } ``` ```javascript= // 箭頭函式寫法: for(let i = 1 ; i<=5 ; i++){ ((n) => { window.setTimeout(()=>{ console.log(i) },1000 * n) })(i) } ``` ::: ## 計時器 ### 原生js步驟 :::spoiler 完整步驟 HTML: ```htmlembedded= <h1 class="textCenter" id="timerCountDown"></h1> <input class="startBtn" type="button" value="開始計時"> ``` JAVASCRIPT: ```javascript= const startBtn = document.querySelector(".startBtn"); let timerCountDown = document.querySelector("#timerCountDown"); let timerMins = 0; let timerSec = 10; let duration = timerMins * 60 + timerSec; let isChick = true; // 填入計時器的數字 function timerNum(num) { return num < 10 ? "0" + num : num; } timerCountDown.textContent = `${timerNum(timerMins)} : ${timerNum(timerSec)}`; // 設定開始倒數計時 function startCoundDown() { let remaining = duration; let mins = 0; let sec = 0; if (isChick) { isChick = false; let countInterval = setInterval(() => { min = parseInt(remaining / 60); sec = parseInt(remaining % 60); remaining--; timerCountDown.textContent = `${timerNum(min)} : ${timerNum(sec)}`; if (remaining < 0) { clearInterval(countInterval); } }, 1000); } } startBtn.addEventListener("click", startCoundDown); ``` ::: >練習: >codepen → [原生js - timer](https://codepen.io/ntjtcxpt-the-animator/pen/vYpYqrg?editors=1010) 1. 綁定DOM ```htmlembedded= <!-- 設定計時器標籤 --> <h1 class="textCenter" id="timerCountDown"></h1> <input class="startBtn" type="button" value="開始計時"> ``` ```javascript= // 宣告開始按鍵 const startBtn = document.querySelector(".startBtn"); // 宣告計時器 let timerCountDown = document.querySelector("#timerCountDown"); ``` 2. 設定==計時器時間== + 分 ```javascript=+ let timerMins = 3; ``` + 秒 ```javascript=+ let timerSec = 30; ``` + ==總持續時間<span style="color:red;font-size:14px;">(以秒為單位)</span>==:3 * 60 <span style="color:gray;font-size:14px;">(3分鐘轉換成180秒)</span> ```javascript=+ let duration = timerMins * 60 + timerSec; ``` 3. 設定==防止重複點擊== ```javascript=+ let isChick = true; ``` 4. 填入計時器的數字 + 設定計時器數字的函式,當數字 < 10,就幫數字前面加上0<span style="color:gray;font-size:14px">(目的是為了讓畫面好看)</span>。 ```javascript=+ function timerNum(num) { return num < 10 ? "0" + num : num; } ``` + 把「分」&「秒」帶入函式裡面,並用 textContent 顯示在畫面上。 ```javascript=+ timerCountDown.textContent = `${timerNum(timerMins)} : ${timerNum(timerSec)}`; ``` 5. 設定開始倒數計時(懶得整理了....反正就跟下面vue步驟二大同小異) ```javascript= function startCoundDown() { let remaining = duration; let mins = 0; let sec = 0; if (isChick) { isChick = false; // 設定每秒執行一次(倒數時間) let countInterval = setInterval(() => { // 取60的整數(就會變成剩幾分鐘) min = parseInt(remaining / 60); // 取60的餘數(就會變成剩幾秒) sec = parseInt(remaining % 60); // 當執行一次 remaining(剩餘時間) 就 -1 remaining--; // 並且更新畫面 timerCountDown.textContent = `${timerNum(min)} : ${timerNum(sec)}`; // 當如果計時時間為0,則跳出倒數計時 if (remaining < 0) { clearInterval(countInterval); } }, 1000); } } startBtn.addEventListener("click", startCoundDown); ``` ### Vue步驟 :::spoiler 完整步驟 ```htmlembedded= <div id="app"> <div class="container"> <div class="timer">{{timerMins}}:{{timerSec}}</div> <input type="button" value="start" @click.default="startTimer"> </div> </div> ``` ```javascript= const app = Vue.createApp({ data() { return { timerMins: 1, timerSec: 10, timer: 0, isClick: true }; }, methods: { startTimer() { if (this.isClick) { this.isClick = false; this.timer = this.timerMins * 60 + this.timerSec; let countInterval = setInterval(() => { this.timerMins = parseInt(this.timer / 60, 10); this.timerSec = parseInt(this.timer % 60, 10); this.timer--; this.timerMins = this.timerMins < 10 ? "0" + this.timerMins : this.timerMins; this.timerSec = this.timerSec < 10 ? "0" + this.timerSec : this.timerSec; if (this.timer < 0) { clearInterval(countInterval); console.log("timeOut"); } }, 1000); } } }, created() { this.timerMins = this.timerMins < 10 ? "0" + this.timerMins : this.timerMins; this.timerSec = this.timerSec < 10 ? "0" + this.timerSec : this.timerSec; } }); app.mount("#app"); ``` ::: >練習: >codepen → [Vue - timer](https://codepen.io/ntjtcxpt-the-animator/pen/zYRjyeL) 1. 設定data :::spoiler 完整步驟 ```javascript= data() { return { timerMins: 1, timerSec: 10, timer: 0, isClick: true }; }, ``` ::: + 分 ```javascript= timerMins: 1, ``` + 秒 ```javascript=+ timerSec: 10, ``` + 總持續時間 ```javascript=+ timer: 0, ``` + 防止重複點擊 ```javascript=+ isClick: true ``` 2. 綁定方法:開始計時 :::spoiler 完整步驟 ```javascript= methods: { startTimer() { if (this.isClick) { this.isClick = false; this.timer = this.timerMins * 60 + this.timerSec; let countInterval = setInterval(() => { this.timerMins = parseInt(this.timer / 60, 10); this.timerSec = parseInt(this.timer % 60, 10); this.timer--; this.timerMins = this.timerMins < 10 ? "0" + this.timerMins : this.timerMins; this.timerSec = this.timerSec < 10 ? "0" + this.timerSec : this.timerSec; if (this.timer < 0) { clearInterval(countInterval); } }, 1000); } } }, ``` ::: + 最外層包上isClick,並馬上將isClick轉為false,這是為了避免重複點擊,造成時間重新被啟動<span style="color:gray;font-size:14px;">(時間呈現方式會很奇怪)</span> ```javascript= startTimer() { if (this.isClick) { this.isClick = false; ... } } ``` 1. 總持續時間賦予值:將總時間轉會成秒數 ```javascript= this.timer = this.timerMins * 60 + this.timerSec; ``` 2. 設定倒數計時 ```javascript= let countInterval = setInterval(() => { ... }, 1000); ``` + 2-1. 轉換「分」、「秒」 :::spoiler 補充:parseInt > parseInt(string, radix):將字串轉換為以十進位表示的整數。 > + string:欲轉換的值。若第一個參數值的類型不是 String,會先使用 ToString 轉換成字串。 > + radix:代表進位系統。依照實作不同可能有不同的預設值,因此建議設定該值,避免造成錯誤 > > 參考文件: > 1.[MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/parseInt) > 2.[認識 parseInt、parseFloat 與 Number 轉換成數字的三種方法](https://medium.com/unalai/%E8%AA%8D%E8%AD%98-parseint-parsefloat-%E8%88%87-number-%E8%BD%89%E6%8F%9B%E6%88%90%E6%95%B8%E5%AD%97%E7%9A%84%E4%B8%89%E7%A8%AE%E6%96%B9%E6%B3%95-276640aedb4e) ::: + 分:總持續時間 / 60 ![](https://i.imgur.com/miAriss.png) + 秒:總持續時間 % 60 ![](https://i.imgur.com/P5RuD7o.png) ```javascript= this.timerMins = parseInt(this.timer / 60, 10); this.timerSec = parseInt(this.timer % 60, 10); ``` + 2-2. 當每進行一次,就==將總持續時間<span style="color:red;">**-1**</span>== ```javascript=+ this.timer--; ``` + 2-3. 當 ==「分」/「秒」< 10== ,就幫數字前面加上0<span style="color:gray;font-size:14px">(目的是為了讓畫面好看)</span>。 ```javascript=+ this.timerMins = this.timerMins < 10 ? "0" + this.timerMins : this.timerMins; this.timerSec = this.timerSec < 10 ? "0" + this.timerSec : this.timerSec; ``` + 2-4. 當==總持續時間 < 0==,就將計時器 <span style="color:red;">**停止**</span> ```javascript= if (this.timer < 0) { clearInterval(countInterval); } ``` 3. 初始畫面:若一開始初始畫面數字 < 0,就幫數字前面加上0。 <span style="color:gray;font-size:14px;">這裡與methods雖然程式碼相同,但用意不同。 created:設定初始畫面 (步驟3) methods:設定進行中畫面 (步驟2-3) </span> ```javascript= created() { this.timerMins = this.timerMins < 10 ? "0" + this.timerMins : this.timerMins; this.timerSec = this.timerSec < 10 ? "0" + this.timerSec : this.timerSec; } ``` >補充~其他有用到類似語法的文章: >使用到setInterval:[新手 JS 地下城 2F — 時鐘](https://medium.com/@cc86418520/%E6%96%B0%E6%89%8B-js-%E5%9C%B0%E4%B8%8B%E5%9F%8E-2f-%E6%99%82%E9%90%98-4813ad10f742)