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