【JavaScript】(トイレの)音姫を作ってみた


こんにちは!Qiita初投稿のim90と申します。現在、フロントエンドエンジニアを目指して勉強中です。

自分で何か作ってみるのが一番勉強になる、とよく聞くので、初めて自作アプリを作ってみました。
今回は、音姫(トイレの音なるやつ)をつくってみました。

全然大した事はしていないのですが、思い出としてこちらに残しておきたいと思います。

実装した機能

  • スタートボタンを押したら音が流れる(25秒)
  • ストップボタンを押したら音が止まる
  • 音が流れている間は、スタートボタンを無効にする
  • ストップ → スタートの際は、改めて25秒間音を流す
  • 残り時間を表示する

シンプルです 
探しましたが本物の音姫の音が無かったので、今回は波の音を使いました。

音が出るデモが無くて申し訳ないです・・・。

コード(HTML、CSS、JavaScript)

index.html
<body>
  <div class="container">
    <div class="contents-wrapper flex-box">
      <!-- speaker -->
      <div class="left">
        ・・・・・・・・・・・
        ・・・・・・・・・・・
        ・・・・・・・・・・・
        ・・・・・・・・・・・
        ・・・・・・・・・・・
        ・・・・・・・・・・・
        ・・・・・・・・・・・
      </div>
      <!-- ↓ display -->
      <div class="right">
        <div class="btn start" onclick="start()"><audio src="sounds/wave1.mp3" loop="true"></audio>&#8680;<br>start</div>
        <div class="btn stop" onclick="stop()"><br>stop</div>
        <div class="remainingTime">残り秒数:<span class="lighting"></span></div>
      </div>
      <!-- ↑ display --> 

    <div class="description">
      <p>手をかざすと25秒水の流れる音がします。
        <br>途中でボタンを押すと延長します。
      </p>
    </div>
  </div>

  <footer>TOTO</footer>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
  <script src="index.js"></script>
</body>
</html>
style.css
*{
    margin: 0 auto;
    padding: 0;
    box-sizing: border-box;
}
body {
    margin: 3rem;
    padding: 2rem;
    width: 30rem;
    height: 20rem;
    text-align: center;
    border: .5px solid lightgrey;
    box-shadow: 1rem 1rem 1rem 1rem rgba(0, 0, 0, .4);
    user-select: none;   
}
.contents-wrapper{
    padding: 1.5rem;
    height: 10rem;
}
.left{
    width: 8rem;
    float: left;
    font-size: 10px;
    padding: 0;
}
.right{
    float: right;
    width: 13rem;
    padding: 1rem 0;
}
.btn{
    height: 3rem;
    width: 6rem;
    margin-bottom: 1rem;
    box-shadow: .1rem .1rem .1rem .1rem rgba(0, 0, 0, .4); 
    cursor: pointer;  
}
.btn:active{
    box-shadow: none;   
}
.start{
    float: left;
    background-color: black;
    color: whitesmoke;
}
.disabled{
    background-color: rgba(0, 0, 0, .8);
    pointer-events: none;
}
.stop{
    border: 1px solid lightgray;
    float: right;
}
.remainingTime{
    clear: both;
    text-align: left;
    font-size: 14px;
}
.description{
    clear: both;
}
footer{
    margin-top: 3.5rem;
}
index.js
var startButton = document.querySelector('.start');
var stopButton = document.querySelector('.stop');
var light = document.querySelector('.lighting');
var audio = new Audio('sounds/wave1.mp3');
audio.loop = true;
var timeoutID;
var playing = true;

//スタートボタンを押したら音を流す
function start(){
    audio.play();
    playing = true;
    light.innerHTML = 25;
    startButton.classList.add("disabled");

    var totalTime = 25000; // 25sec
    var oldTime = Date.now();

    var timerID = setInterval(function(){        
        var currentTime = Date.now();
        var diff = currentTime - oldTime;

        var remainMsec = totalTime - diff;
        var remainSec = Math.ceil(remainMsec / 1000);
        light.innerHTML = remainSec;

        if(remainSec <= 0){
            clearInterval(timerID);
            light.innerHTML = "";
            startButton.classList.remove("disabled");
        }
        if(playing === false){
            clearInterval(timerID);
            light.innerHTML = "";
        }
    }, 1000);
    timeoutID = window.setTimeout(() => {
        audio.pause();
        audio.currentTime = 0;
    }, 25000);
}

//ストップボタンを押したら音をストップさせる
function stop(){
    audio.pause();
    clearTimeout(timeoutID);
    audio.currentTime = 0;
    playing = false;
    startButton.classList.remove("disabled");
}

作成フロー(JavaScript)

  1. 変数の定義
  2. スタートボタンを押すと音が流れる関数をつくり、25秒でタイムアウトするようにする(setTimeout())
  3. ストップボタンを押すと音が止まる関数をつくる
  4. 再生時はスタートボタンを無効にする(このままだとスタート関数が重複して実行できてしまうため)
  5. ストップボタンを押して再生したときに、最初から音が流れる(clearTimeout())この作業をしないと、ストップ → 再生したときに音が中途半端に切れてしまいました。

作ってみて

実は今回現役プログラマーの方に見て頂いたのですが、下記のアドバイスを頂きました。

  • onclickよりかは、addEventListenerを使った方が見やすい。(どの要素と関数が紐付いているかが一目で分かりやすいため)
  • 現場では==よりも===を使う事が求められる。こちらの記事で分かりやすく説明されていました。JavaScript 忘れがちな === と == の違い

あとは、varでなくletconstに書き直せば良かったな~、と今更ながら思いました・・・

また、スピーカーの見た目を再現するのに・・・を何個も書いたのですが、もっと良い方法があるような気もします。

お気づきの点がございましたら、もっとこうした方が良いなど、ぜひアドバイス頂けますと幸いです。

とりあえず今は次のアプリに挑戦してみようと思います!ご覧頂き、ありがとうございました。