【JavaScript】カウントダウンタイマー実装

## 最初に
カウントダウンタイマーを実装する学習をしたので、学んだことを記事にしたいと思います。
今回はJavaScriptを使ってシンプルなカウントダウンタイマーを実装します。
カウントダウンタイマーは、特定の時間が経過するまでの経過時間を表示するツールで、イベントのカウントダウンやプロモーションの終了時刻の通知など、様々な場面で活用できます。

## 目次
[1．完成形](#完成形)
[2．HTML、CSSの実装](#2htmlcssの実装)
[3．JavaScriptの実装](#3javascriptの実装)
　　∟[3.1.タイマーの設定](#31タイマーの設定)
　　∟[3.2．残り時間の計算](#32残り時間の計算)
　　∟[3.3．タイマーの更新と表示](#33タイマーの更新と表示)
[5．実際に動かしてみよう](#5実際に動かしてみよう)
[6．応用編：イベントのカウントダウン](#6応用編イベントのカウントダウン)
[7．まとめ](#7まとめ)

## 1．完成形
タイマーを1時間にセットして、終了時に「終了しました」と表示されます。
▼カウント開始
![無題の動画 ‐ Clipchampで作成.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3148670/7900a1c3-4db3-6997-cd2d-f4876060d3f2.gif)

▼カウント終了
![無題の動画 ‐ Clipchampで作成 (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3148670/0bb9b0e3-f982-2a62-483a-5fda915c3387.gif)

## 2．HTML、CSSの実装
※HTML、CSSの解説は割愛させていただきます。
```HTML:HTML
<!DOCTYPE html>
<html lang="js">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>カウントダウンタイマー</title>
    <link rel="stylesheet" href="/CSS/style.css">
</head>
<body>
    <div class="timer" id="countdown">00:00:00</div>
</body>
<script src="/JS/script.js"></script>
</html>
```
```CSS:CSS
body{
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    font-family: Arial, sans-serif;
    background-color: #f4f4f9;
}

.timer{
    text-align: center;
    font-size: 2em;
    padding: 20px;
    border: 2px solid #ccc;
    border-radius: 10px;
    background-color: #ffff;
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
```
ここまで作業をすると以下の画像のように表示されます。

![スクリーンショット 2024-07-31 222824.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3148670/dd87afc2-80ad-9c96-d020-cbb4392963e2.png)

## 3．JavaScriptの実装
### 3.1.タイマーの設定

**script.js**を以下のように編集します。
```JavaScript:JavaScript
window.onload = function(){
    const countDown = document.getElementById('countdown'); 
    const targetTime = new Date().getTime() + 3600000; 
}
```
### ▼解説
カウントダウンを表示する要素を定数``countDown``に格納。
```JavaScript:JavaScript
const countDown = document.getElementById('countdown');
```
タイマーの時間を1時間に設定し、定数``targetTime``に格納。
```JavaScript:JavaScript
const targetTime = new Date().getTime() + 3600000; 
```
:::note info
new Date()オブジェクト
新しい日付オブジェクトを生成、現在の日付と時刻を取得。
:::

:::note info
getTime()メソッド
Dateオブジェクトからミリ秒単位のタイムスタンプを取得するメソッド。
1970年1月1日00:00:00 UTCから経過時間（ミリ秒）として表されます。
:::

### 3.2．残り時間の計算
**script.js**を以下のように編集します。
```diff_JavaScript:JavaScript
window.onload = function(){
    const countDown = document.getElementById('countdown'); 
    const targetTime = new Date().getTime() + 3600000; 

+    function updateCountDown(){
+        const now = new Date().getTime();
+        const distance = targetTime - now;

+        const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+        const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
+        const seconds = Math.floor((distance % (1000 * 60)) / 1000);
     }
}
```
### ▼解説
残り時間の計算、画面に残り時間を表示する関数``updateCountDown()``を定義します。
それでは関数内の処理について解説していきます。

**1．現在の時刻をミリ秒で取得し、定数``now``に格納。**
**2．設定した時間（``targetTime`` ）までの残り時間を計算し、定数``distance``に格納**

定数``distance``を使って、後続の処理で残り時間を計算したり、タイマーが終了したかどうかをチェックします。
```JavaScript:JavaScript
const now = new Date().getTime(); 
const distance = targetTime - now;
```

**3.残り時間を「時間」に変換。**

``(distance % (1000 * 60 * 60 * 24))``では、distance（設定した時間までの残り時間）を1日のミリ秒数（1000ミリ秒 * 60秒 * 60分 * 24時間）で割った余りを求めます。これにより、1日未満の残り時間が求められます。
次に1日未満の残り時間を1時間のミリ秒数（1000ミリ秒 * 60秒 * 60分）で割ります。これにより、残り時間の「時間」の部分が求められます。
最後に``Math.floor()``メソッドで小数点以下を切り捨てて整数部分のみを定数``hours``に格納します。
```JavaScript:JavaScript
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
```

**4.残り時間を「分」に変換。**

``(distance % (1000 * 60 * 60))``では、distanceを1時間のミリ秒数（1000ミリ秒 * 60秒 * 60分）で割った余りを求めます。これにより、1時間未満の残り時間が得られます。
次に1時間未満の残り時間を1分のミリ秒数（1000ミリ秒 * 60秒）で割ります。これにより、残り時間の「分」の部分が得られます。
最後に``Math.floor()``メソッドで小数点以下を切り捨てて整数部分のみを定数``minutes``に格納します。
```JavaScript:JavaScript
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
```
**6．残り時間を「秒」に変換。**

``(distance % (1000 * 60))``では､distanceを１分のミリ秒で割って余りを求めます。これにより、１分未満の残り時間が得られます。
次に１分未満の残り時間を１秒のミリ秒数（1000ミリ秒数）で割ります。これにり、残り時間の「秒」の部分が得られます。
最後に``Math.floor()``メソッドで小数点以下を切り捨てて整数部分のみを定数``seconds``に格納します。
```JavaScript:JavaScript
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
```

<details>
<summary>計算の流れを確認したい方はこちらを開いてください。</summary>

**▼計算して流れをみよう**

実際に計算して流れをみてみましょう。
残り時間が59分59秒と仮定して計算します。

※残り時間 ＝ 3599000ミリ秒

**1．「時間」の計算**

・残り時間を1日のミリ秒数で割った余りを出す。
3599000 % 86400000 = 3599000

・余りを1時間のミリ秒数で割る。
3599000 / 3600000 = 0.9983333333333333

・Math.floorで小数点以下を切り捨てる。
Math.floor(0.9997222222222222) = 0

当たり前ですが、残り時間が59分59秒なので答えは「0時間」です。

**2．「分」の計算**

・残り時間を1時間のミリ秒数で割った余りを出す。
3599000 % 3600000 = 3599000

・余りを1分のミリ秒数で割る。
3599000 / 60000 = 59.98333333333333

・Math.floorで小数点以下を切り捨てる。
Math.floor(59.98333333333333) = 59

残り時間の「分」は「59分」です。

**「秒」の計算**

・残り時間を1分のミリ秒数で割った余りを出す。

3599000 % 60000 = 59.000

・余りを1秒のミリ秒数で割る。

59.000 / 1000 = 59

・Math.floorで小数点以下を切り捨てる。
Math.floor(59) = 59

残り時間の「秒」は「59秒」です。

これで残り時間59分59秒を求めることができました。
</details>

### 3.3．タイマーの更新と表示
**script.js**を以下のように編集します。
```diff_JavaScript:JavaScript
window.onload = function(){
    const countDown = document.getElementById('countdown'); 
    const targetTime = new Date().getTime() + 3600000; 

    function updateCountDown(){
        const now = new Date().getTime();
        const distance = targetTime - now;

        const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
        const seconds = Math.floor((distance % (1000 * 60)) / 1000);

+       countDown.textContent = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;

+        if(distance < 0){
+            clearInterval(interval);
+            countDown.textContent = '終了しました';
+        }
     }

+    const interval = setInterval(updateCountDown, 1000);
+     updateCountDown();

}
```

**1．タイマーの更新**
計算された残り時間をブラウザに表示します。
``String()``関数で文字列に変更、``padStart()``関数で文字列が2文字に満たない場合、先頭に「0」を追加して2文字にします。これにより、ブラウザに残り時間が表示されます。

※テンプレートリテラルの解説は割愛します。
```JavaScript:JavaScript
countDown.textContent = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
```
**2．タイマーの判定**

残り時間が「0」未満の場合、停止して「終了しました。」と表示します。
```JavaScript:JavaScript
if(distance < 0){
    clearInterval(interval);
    countDown.textContent = '終了しました';
  }
```
:::note info
clearInterval()メソッド
setInterval()で呼び出した繰り返し動作を取り消します。
:::
**3．一定間隔で関数``updateCountDown()``を更新**

``setInterval()``メソッドを使い、1秒毎に``updateCountDown()``を呼び出し、残り時間の計算、更新、終了判定を行います。
```JavaScript:JavaScript
const interval = setInterval(updateCountDown, 1000);
```
:::note info
setInterval()メソッド
任意の間隔で関数やコードスニペットを繰り返し呼び出す。
:::
## 5．実際に動かしてみよう
▼完成コード
```diff_JavaScript:JavaScript
window.onload = function(){
    const countDown = document.getElementById('countdown'); 
    const targetTime = new Date().getTime() + 3600000; 

    function updateCountDown(){
        const now = new Date().getTime();
        const distance = targetTime - now;

        const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
        const seconds = Math.floor((distance % (1000 * 60)) / 1000);

       countDown.textContent = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;

        if(distance < 0){
            clearInterval(interval);
            countDown.textContent = '終了しました';
        }
     }

    const interval = setInterval(updateCountDown, 1000);
     updateCountDown();

}
```
ブラウザでindex.htmlを開き、カウントダウンが動作する様子を確認しましょう。設定した時間までの経過がリアルタイムで表示されます。

## 6．応用編：イベントのカウントダウン
ショッピングサイトなどで**セール終了まで残り〇〇時間**の案内を見かける事があるかと思います。今回はこちらを実装します。

それでは先ほどの完成コードを以下のように編集します。

```diff_JavaScript:JavaScript
window.onload = function(){
    const countDown = document.getElementById('countdown'); 
-    const targetTime = new Date().getTime() + 3600000; 
+    const eventTime = new Date('2024-08-04T17:00:00').getTime();

    function updateCountDown(){
        const now = new Date().getTime();
-        const distance = targetTime - now;
+        const distance = eventTime - now;

+       const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
        const seconds = Math.floor((distance % (1000 * 60)) / 1000);

-       countDown.textContent = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
+       countDown.textContent = `残り${days}日${hours}時間${minutes}分${seconds}秒`;

        if(distance < 0){
            clearInterval(interval);
-           countDown.textContent = '終了しました';
+           countDown.textContent = 'セールは終了しました';
        }
     }

    const interval = setInterval(updateCountDown, 1000);
     updateCountDown();

}
```

**▼解説**
1．イベント終了時間を設定
``new Date()``オブジェクトで未来日（イベント終了時間）の日付、時間を取得します。
```JavaScript:JavaScript
const eventTime = new Date('2024-08-04T17:00:00').getTime();
```
2．イベント終了までの時間を計算
**イベント終了時間 － 現在の時間**とすることでイベント終了時間を求めます。
```JavaScript:JavaScript
const distance = eventTime - now;
```
3.日数を取得
応用編では残りの日数も表示します。
残り時間を1日のミリ秒数で割ります。これにより、残りの「日数」が求められます。
```JavaScript:JavaScript
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
```
4.表示形式
日数を表示できるように計器を変更します。
```JavaScript:JavaScript
countDown.textContent = `残り${days}日${hours}時間${minutes}分${seconds}秒`;
```

最後に``updateCountDown()``関数を呼び出すことで設定した時間までの経過がリアルタイムで表示されます。

▼イメージ
![無題の動画 ‐ Clipchampで作成 (3).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3148670/1b95df87-f1e3-31af-5571-fa2c7cfbbdd8.gif)

![無題の動画 ‐ Clipchampで作成 (6).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3148670/88018915-23d5-ee89-3d43-52e9c05e37c4.gif)

## 7．まとめ
この記事では、JavaScriptを使って基本的なカウントダウンタイマーを作成する方法を紹介しました。これを応用することで様々な用途に活用できると思います。ぜひ、いろいろなシーンで使ってみてください。

ご意見などありましたらコメントください！
他の便利な機能や改善案もお待ちしております！