CodeCampのJavaScript編も残りわずか。今回は、これまでの知識の習熟度合いを見るための課題「カウントダウンタイマー」を作ってみたのでご紹介します。
カウントダウンタイマーの仕様
- カウントは03:00からスタートする
- スタートボタンを押すと1秒ずつカウントが進む
- カウントが00:00になったら「Time UP!」と表示する
- ストップボタンを押すとカウントが止まる
- リセットボタンを押すとカウントが03:00に戻り、カウントが止まる
どこにでもある、ごく普通のカウントダウンタイマーですね。
意外にも、カウントの処理は簡単だったのですが、スタートボタンを2度クリックすると、秒数早く進んでしまったり、2度クリックしたらストップボタンが効かなくなったり、予想外?のアクションが起こったときの処理の仕方が少し難しかったです。
サンプルコード(HTML・スクリプト)
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>カウントダウンタイマー</title> <link rel="stylesheet" href="stop_watch.css"> <script> var count = 180; //カウントダウンの数字を格納する変数 var min = 0; //残り時間「分」を格納する変数 var sec = 0; //残り時間「秒」を格納する変数 var stp = null; //setInerval・clearInervalを制御する変数 var i = 0; //ボタンの2回クリック等禁止イベントを制御する変数 //1000ミリ秒毎にcont_down関数を呼び出す function count_start(){ i++; if(i === 1){ stp = setInterval(count_down,1000); } else { alert("2度押し厳禁やで!"); i = 0; } } //カウンドダウン表示 function count_down(){ if(count === 1){ var display = document.getElementById("default"); display.innerHTML = "TIME UP!"; clearInterval(stp); } else { count--; min = parseInt(count / 60); sec = count % 60; var count_down = document.getElementById("default"); count_down.innerHTML = ("0"+min).slice(-2) +":" + ("0"+sec).slice(-2); } } //ストップボタンクリック時のアクション function count_stop(){ clearInterval(stp); i = 0; } //リセットボタンクリック時のアクション function count_reset(){ count = 180; min = parseInt(count / 60); sec = count % 60; i = 0; var count_down = document.getElementById("default"); count_down.innerHTML = ("0"+min).slice(-2) +":" + ("0"+sec).slice(-2); } //ボタンイベント制御 window.onload = function(){ var start = document.getElementById('start'); var stop = document.getElementById("stop"); var reset = document.getElementById("reset"); start.addEventListener('click',count_start, false); stop.addEventListener("click",count_stop, false); reset.addEventListener("click",count_reset,false); } </script> </head> <body> <div id="body"> <div id ="box"> <p id="default">03:00</p> <button id="start">スタート</button> <button id="stop">ストップ</button> <button id="reset">リセット</button> </div> </div> </body> </html>
サンプルコード(スタイルシート・CSS)
#body{ width:410px; height:290px; border-radius: 10px; border:solid 1px #000000; margin:100px; background:-webkit-gradient(linear, left top, left bottom, from(#2f4f4f), to(#066)); } p#default { width:340px; height:100px; background-color:#f9f9f9; border-radius: 10px; border:solid 4px #708090; margin-top:25px; margin-left:25px; margin-right:25px; margin-bottom:25px; padding-top:50px; padding-bottom:5px; color:#535353; font-size:40px; font-weight:bold; text-align:center; } #start{ width:100px; height:50px; margin-left:30px; color:#535353; font-size:18px; font-weight:bold; } #stop{ width:100px; height:50px; margin-left:19px; margin-right:19px; color:#535353; font-size:18px; font-weight:bold; } #reset{ width:100px; height:50px; color:#535353; font-size:18px; font-weight:bold; }
ポイント
- 「setInterval」メソッド(一定の時間間隔で任意の関数を実行)で秒数をカウントする
- if文で条件分岐し、カウントが0になったら「TIME UP!」を表示する
- 数字の表示はdocument.writeではなく、HTMLのp要素に入れて表示する。
- ストップボタンは、「setInterval」を停止する「clearInterval」メソッドで処理を停止する。
- リセットボタンクリック時には、count、min、sec変数を使って初期値を計算し、p要素に入れて表示する。
- ボタンの制御は、イベントハンドラを登録して1つの関数にまとめる。
細かい説明は省いていますが、だいたいこんな感じでしょうか。
コードを書いているときに、よく起こったのは、変数名が予約語とかぶって正しく書けているのに動かないということ。頭ではわかっていても、意外とやってしまう間違い。変数、関数の命名はなかなか難しい…。
あと、少しややこしいのは、数字の頭に表示する「0」の文字。数字の「3」を、「03」と表示させるためには、「3」の前に「0」を埋める処理が必要です。これは「0埋め」とか「ゼロパディング」と呼びます。
こちらのページに詳しく解説されていたので、参考までにリンクを貼っておきます。
あともう一点。少しわかりづらいグローバル変数iの役割ですが、ボタンが何度も押されると、予期しない動作が起こることがあります。
例えば、スタートボタンを2回クリックすると、1000ミリ秒間隔でcount_down関数が実行されるという処理が、何度も繰り返されてしまい、カウントダウンの速度がはやくなりました。その他にも、特定の動作を行ったときにおかしな動作が発生しました。
上記のようなおかしな動作が起こらないように、変数iを使って制御しています。
変数iがないバージョンのサンプルコードを以下に記載したので、興味がある方は、ソースをコピーして動かしてみてください。
サンプルコード(グローバル変数iなし)
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>カウントダウンタイマー</title> <link rel="stylesheet" href="stop_watch.css"> <script> var count = 180; var min = 0; var sec = 0; var stp = null; //1秒毎にcont_down関数を function count_start(){ stp = setInterval(count_down,1000); } //カウンドダウン表示 function count_down(){ if(count === 0){ var display = document.getElementById("default"); display.innerHTML = "TIME UP!"; } else { count--; min = parseInt(count / 60); sec = count % 60; var count_down = document.getElementById("default"); count_down.innerHTML = ("0"+min).slice(-2) +":" + ("0"+sec).slice(-2); } } function count_stop(){ clearInterval(stp); } function count_reset(){ count = 180; min = parseInt(count / 60); sec = count % 60; var count_down = document.getElementById("default"); count_down.innerHTML = ("0"+min).slice(-2) +":" + ("0"+sec).slice(-2); } //イベント管理 window.onload = function(){ var start = document.getElementById('start'); var stop = document.getElementById("stop"); var reset = document.getElementById("reset"); start.addEventListener('click', count_start, false); stop.addEventListener("click",count_stop, false); reset.addEventListener("click",count_reset,false); } </script> </head> <body> <div id="body"> <div id ="box"> <p id="default">03:00</p> <button id="start">スタート</button> <button id="stop">ストップ</button> <button id="reset">リセット</button> </div> </div> </body> </html>
カウントダウンタイマーの作り方は以上です。
これで、CodeCampのJavaScript編の大きな課題のうちの1つが終了しました。ここは結構苦戦したので、10時間くらいはコードと睨めっこしていましたw
1人ではどうしても解決できなかったので、レッスン2回分を消費して無事解決。
講師の人に話を聞いて良かったのは、独学では気付きにくい実践で使える知恵を吸収できたことでした。
例えば、コードが動かないときに、間違っている部分を特定する方法。
関数にalert()を入れることで、正しく処理が行われている部分、行われていない部分をチェックし、間違いを特定する、あるいは、GoogleChromeの要素の検証を行って、コードのエラーを検証する、などなど。
コードの書き方や知識については、ググればわかりますが、上記のような、作業を進める上での知恵を書いてくれているサイトってあんまりないんですよね。
コードを書いていると、「一見正しいように見えるけど、実はどこかが間違っている」という場面に直面し、おかしなコードを探すという作業が頻繁に発生していたので、講師の方に教えて貰った知恵はとても役に立ちました。
独学でやるのもアリですが、効率的に学ぶには、その道のプロに聞くことも必要だと実感した次第です。
この課題が完成してから、自分の中で少し変化があって、プログラムに対する変な抵抗感がなくなりました。これまでは、コードを書くときに、自信がなかったというか、難しく考えすぎてしまって、手が止まってしまうことがよくあったのですが、徐々にそれがなくなってきたように思います。
少しずつ理解が進んできた感触があるので、引き続き、いろんなものを実際に作ってみて、引き出しを増やしていこうと思います。