こんにちは。KOUKIです。
本記事は、Udemyの「50 Projects In 50 Days – HTML, CSS & JavaScript」で学習したことを載せています。
<目次>
実装するもの
今回は、カウントダウンアプリをJavaScriptで実装したいと思います。
demoは「こちら」で確認できます。
環境構築
簡単な環境構築をお願いします。
必要なファイルは、以下の通りです。
1 2 3 4 5 |
$ tree . ├── index.html ├── script.js └── style.css |
CSS版
ページ(HTML)の作成
最初にページを作成しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="style.css" /> <title>Animated Countdown</title> </head> <body> <div class="counter"> <div class="nums"> <span class="in">3</span> <span>2</span> <span>1</span> <span>0</span> </div> <h4>Get Ready</h4> </div> <div class="final"> <h1>GO</h1> <button id="replay">Replay</button> </div> <script src="script.js"></script> </body> </html> |
このHTMLをブラウザ上で表示すると以下のようになります。

スタイル(CSS)を装飾
次にスタイルを記述しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); * { box-sizing: border-box; } body { font-family: 'Roboto', sans-serif; margin: 0; height: 100vh; overflow: hidden; } h4 { font-size: 20px; margin: 5px; text-transform: uppercase; } .counter { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; } .counter.hide { transform: translate(-50%, -50%) scale(0); animation: hide 0.2s ease-out; } @keyframes hide { 0% { transform: translate(-50%, -50%) scale(1); } 100% { transform: translate(-50%, -50%) scale(0); } } .final { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0); text-align: center; } .final.show { transform: translate(-50%, -50%) scale(1); animation: show 0.2s ease-out; } @keyframes show { 0% { transform: translate(-50%, -50%) scale(0); } 30% { transform: translate(-50%, -50%) scale(1.4); } 100% { transform: translate(-50%, -50%) scale(1); } } .nums { color: #3498db; font-size: 50px; position: relative; overflow: hidden; width: 250px; height: 50px; } .nums span { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) rotate(120deg); transform-origin: bottom center; } .nums span.in { transform: translate(-50%, -50%) rotate(0deg); animation: goIn 0.5s ease-in-out; } .nums span.out { animation: goOut 0.5s ease-in-out; } @keyframes goIn { 0% { transform: translate(-50%, -50%) rotate(120deg); } 30% { transform: translate(-50%, -50%) rotate(-20deg); } 60% { transform: translate(-50%, -50%) rotate(10deg); } 100% { transform: translate(-50%, -50%) rotate(0deg); } } @keyframes goOut { 0% { transform: translate(-50%, -50%) rotate(0deg); } 60% { transform: translate(-50%, -50%) rotate(20deg); } 100% { transform: translate(-50%, -50%) rotate(-120deg); } } |
ここまで実装すると以下のようになります。

demoのカウントダウン切り替えのキーは、「transform-origin」です。これで、切り替え時の原点を指定しています。
JavaScriptの実装
それでは、JavaScriptを実装していきましょう。
カウントダウンのアニメーションは、CSSがほぼやってくれます。
カウントダウンの動きは、以下のspan要素に「”in” or “out”」クラスを付け替えることによって実現します。
1 2 3 4 5 6 7 8 9 |
<div class="counter"> <div class="nums"> <span class="in">3</span> <span>2</span> <span>1</span> <span>0</span> </div> <h4>Get Ready</h4> </div> |
そして、最終的に、finalクラスが付与されたHTML要素に「show」クラスを追加することで、「Go」が表示されます。
1 2 3 4 |
<div class="final"> <h1>GO</h1> <button id="replay">Replay</button> </div> |
このような流れです。
要素の取得
最初に、画面操作に必要な要素を取得します。
1 2 3 4 5 |
// 要素の取得 const nums = document.querySelectorAll('.nums span') const counter = document.querySelector('.counter') const finalMessage = document.querySelector('.final') const replay = document.querySelector('#replay') |
カウントダウン実行関数の定義
次は、カウントダウンを実行する関数を定義しましょう。
1 2 3 4 5 6 7 8 9 |
// アニメーション実行 runAnimation() function runAnimation() { // span要素の数だけ実行 nums.forEach((num, idx) => { console.log(num, idx) }) } |
上記では、取得したspan要素分だけ、処理を実行します。

animationendイベントの登録
次は、animationendイベントを登録します。これは、その名の通り、CSSのアニメーションが終了した時に発火されるイベントです。
CSSのアニメーションとして、「goIn」、「goOut」を設定しているので、これらが終了したら実行されるということです。
1 2 3 4 5 6 7 8 9 10 |
function runAnimation() { // span要素の数だけ実行 nums.forEach((num, idx) => { // animationendイベントの登録 num.addEventListener('animationend', (e) => { ... }) }) } |
条件によってクラスの付け替えを行う
前述の通り、特定のクラスをHTML要素につけることで、カウントダウン処理を実現します。
条件は全部で3つあるので、以下のように実装しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
function runAnimation() { // span要素の数だけ実行 nums.forEach((num, idx) => { // 配列数を取得 const nextToLast = nums.length - 1 num.addEventListener('animationend', (e) => { // goInが終了し、かつ要素が最後ではない場合 if(e.animationName === 'goIn' && idx !== nextToLast) { // classの付け替え num.classList.remove('in') num.classList.add('out') } else if( // goOutが終了し、かつ、指定要素の"次の要素"が存在している場合 e.animationName === 'goOut' && num.nextElementSibling) { // classの付け替え num.nextElementSibling.classList.add('in') } else { // カウントダウンが完了した場合 // classの付け替え counter.classList.add('hide') finalMessage.classList.add('show') } }) }) } |
ここまで実装するとアニメーションが動きます。
リセット機能の実装
最後に、カウントダウンをリセットする機能を作成しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function resetDOM() { // classを除去 counter.classList.remove('hide') finalMessage.classList.remove('show') nums.forEach((num) => { // classを除去 num.classList.value = '' }) // <span class>3</span>にinをつける nums[0].classList.add('in') } // Replayボタンのクリックイベント replay.addEventListener('click', () => { resetDOM() runAnimation() }) |
これで、Replayボタンを押下したときに、初期状態に戻しつつ、カウントダウンをやり直してくれるようになります。
これで、完成です。
おわりに
今回の講義も大変勉強になりました。カウントダウンアプリって、ありそうでないですよね。
CSSのアニメーションとJavaScriptを組み合わせれば、かなり色々なアプリケーションが作れそうです。
やはり、JavaScriptの次は、CSSを学ぶか…
それでは、また!
JavaScriptまとめ
JavaScript ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
// 要素の取得 const nums = document.querySelectorAll('.nums span') const counter = document.querySelector('.counter') const finalMessage = document.querySelector('.final') const replay = document.querySelector('#replay') // アニメーション実行 runAnimation() function runAnimation() { // span要素の数だけ実行 nums.forEach((num, idx) => { // 配列数を取得 const nextToLast = nums.length - 1 // animationendイベントの登録 num.addEventListener('animationend', (e) => { // goInが終了し、かつ要素が最後ではない場合 if(e.animationName === 'goIn' && idx !== nextToLast) { // classの付け替え num.classList.remove('in') num.classList.add('out') } else if( // goOutが終了し、かつ、指定要素の"次の要素"が存在している場合 e.animationName === 'goOut' && num.nextElementSibling) { // classの付け替え num.nextElementSibling.classList.add('in') } else { // カウントダウンが完了した場合 // classの付け替え counter.classList.add('hide') finalMessage.classList.add('show') } }) }) } function resetDOM() { // classを除去 counter.classList.remove('hide') finalMessage.classList.remove('show') nums.forEach((num) => { // classを除去 num.classList.value = '' }) // <span class>3</span>にinをつける nums[0].classList.add('in') } // Replyボタンのクリックイベント replay.addEventListener('click', () => { resetDOM() runAnimation() }) |
コメントを残す
コメントを投稿するにはログインしてください。