maesblog

HTML5 Audio Playerを改良しました: ボリューム機能追加、再生ボタン機能拡張など

2年前に『HTML5のaudio要素とJavaScriptで作るオーディオプレイヤー』という記事を書きました。今回その記事内でサンプルとして作ったオーディオプレイヤーを改良してみたので、少々解説でもしてみたいと思います。

主な改良点は以下の2点。まずはサンプルでご確認ください。

  • ボリューム機能の追加
  • 再生/一時停止ボタンの一体化

HTML5 Audio Playerサンプル

HTML5 Audio Player

まず、ボリューム機能ですが、スライダーを左右に動かすと、再生中の曲のボリュームを変更できるようになっています。それからボタンに関して言うと、「再生」ボタンを押すと曲が再生されるのと同時に「一時停止」ボタンに切り替わり、さらに「一時停止」ボタンを押すと曲が一時停止されるのと同時に「再生」ボタンに切り替わるようになっています。(使っている曲は私の10年前に作ったデモ曲となります。何卒ご了承ください…)

それでは、さっそく解説に入ります。必要に応じて『HTML5のaudio要素とJavaScriptで作るオーディオプレイヤー』も参考にしてください。

ボリューム機能の実装

ボリューム機能の実装は以下の通りです。まずHTML側でボリューム用のスライダーを作り、JavaScript側でスライダーの値を取得してAudio要素のvolumeプロバティにセットして制御します。

スライダーの作成

まずスライダーを何で作ろうかと悩んだんですが、ちょうど現バージョンのFirefox 23でも対応がなされた「input要素のtype=”range”」を使ってみることにしました。type=”range”はすでにFirefox以外の主要ブラウザでは対応済みとなっているので、今回のFirefoxの実装でほぼ問題なく使えるようになっています。

実装は以下の通りです。ボリュームの最小値最大値を、それぞれmin属性max属性で指定しています。それからボリュームの初期値value属性で指定しています。

<input type="range" id="volume" value="50" min="0" max="99">

上のソースをブラウザで表示すると以下のように表示されます。

スライダーの値の取得と、Audio要素のvolumeプロパティへの指定

スライダーを作ったら、次はスライダーで指定した値を取得し、Audio要素のvolumeプロパティにセットするようにします。

スライダーの値の取得は「change」イベントを使って取得します。

var volume = document.getElementById('volume');

volume.addEventListener('change', function () {
  // changeイベントが発生した時の処理
}, false);

値は、input要素をgetElementByIdを使ってオブジェクトに返し、valueプロバティで取得します。その際にAudio要素のvolumeプロバティに指定できる値が0〜1までとなっているので、取得した値を1以下の数値に変更してあげる必要があります。今回は0〜99までの数値を取得するようにしているので、以下のように取得した値が1桁であれば「0.0」を、それ以外であれば「0.」を値の前につけてあげるようにします。

var volumeValue = (volume.value.length == 1) ? '0.0' + volume.value : '0.' + volume.value;

取得した値を以下のようにしてAudio要素のvolumeプロパティにセットします。

myAudio.volume = volumeValue;

上記まとめると以下のようになります。

var myAudio = new Audio('');
var volume = document.getElementById('volume');

volume.addEventListener('change', function () {
  var volumeValue = (volume.value.length == 1) ? '0.0' + volume.value : '0.' +
    volume.value;
  myAudio.volume = volumeValue;
}, false);

参考にしたサイトは以下となります。

再生/一時停止ボタンの一体化の実装

前回のオーディオプレイヤーでは、それぞれ「再生」「一時停止」「停止」ボタンは独立して設置していました。今回は利便性を考えて、「再生」ボタンと「一時停止」ボタンを交互に切り替えるようにしました。それから「停止」ボタンは見せ方を変えて「スタートに戻る」ボタンとしました。

ボタンのデザイン

ボタンはCSS3を使ってデザインしています。アイコンはFont Awesomeを使っています。Font Awesomeはご存知の方も多いと思いますが、簡単に言えば「Webフォントのようなアイコン」となります。CSSでアイコンの色やサイズなどをコントロールすることができます。詳細はFont Awesomeのサイトをご参照ください。

まずHTML部分は以下の通りです。Font Awesomeのアイコンをspan要素で囲んでいます。(i要素のclass属性の値を変更することでアイコンを切り替えることができるようにしています。)

<div id="audio_button">
  <span id="stop" class="button"><i class="icon-step-backward"></i></span>
  <span id="play" class="button"><i class="icon-play"></i></span>
</div>

次にCSSの部分は以下の通りです。特記すべきところとしては、「border-radius: 50%;」として指定要素を丸くしています。それから「background-image: linear-gradient(-45deg, #e7e7e7 50%, #fff 50%, #fff);」として斜めの2色背景を実現させています。

.button {
  display: inline-block;
  width: 40px;
  margin: 0 5px;
  line-height: 40px;
  text-align: center;
  background-color: #fff;
  background-image: linear-gradient(-45deg, #e7e7e7 50%, #fff 50%, #fff);
  background-image: -webkit-linear-gradient(-45deg, #e7e7e7 50%, #fff 50%, #fff);
  border-radius: 50%;
  box-shadow: 0 0 3px #222;
  font-size: 16px;
  text-shadow: inset 0 1px 1px #222;
  cursor: pointer;
}

上のソースをブラウザで表示すると以下のように表示されます。

ボタンの切り替え処理の実装

ボタンの切り替えは、関数を作り、その関数を実行した際に上記のHTMLのボタンのspan要素のid属性とアイコンのi要素のclass属性を変更するようにしています。

// class="button"と指定された2番目の要素を取得
var buttonType = document.getElementsByClassName('button')[1];
// 上記要素の子要素となっているi要素を取得
var buttonIcon = buttonType.childNodes[0];

// ○○の部分を状況に応じて「icon-play」、「icon-pause」と指定する。
// Font Awesomeが指定されたclass属性によって切り替わる。
buttonIcon.setAttribute('class', '○○');

// ○○の部分を状況に応じて「play」、「pause」と指定する。
// ここのid属性はAudioの再生処理に関わってくる。
buttonType.setAttribute('id', '○○');

上記の基本パターンを元に、以下のような関数を作成しています。引数の値によって、処理を切り替えるようにしています。

// 再生時は「play」、一時停止時は「pause」と引数にセットして
// 関数を実行するようにします。
function changeButton(button) {
  var buttonType = document.getElementsByClassName('button')[1];
  var buttonIcon = buttonType.childNodes[0];

  switch(button) {
    // 引数が「play」だった場合(「再生」ボタンが押された場合)
    case 'play':
      buttonIcon.setAttribute('class', 'icon-pause');
      buttonType.setAttribute('id', 'pause');
      break;
    // 引数が「pause」だった場合(「一時停止」ボタンが押された場合)
    case 'pause':
      buttonIcon.setAttribute('class', 'icon-play');
      buttonType.setAttribute('id', 'play');
      break;
    // 引数が「play」「pause」以外だった場合
    default:
      buttonIcon.setAttribute('class', 'icon-play');
      buttonType.setAttribute('id', 'play');
      break;
  }
}

関数の実行

あとは上記で作成した関数を、状況に応じて実行させてあげます。今回は「ボタンが押されたとき」「曲が選択された時」「曲が終了した時」に実行するようにしています。

ここで特殊なイベントとして「曲が終了した時」のイベントの所得方法を紹介しておきます。曲の終了は「ended」イベントで取得することができます。

var myAudio = new Audio('');

myAudio.addEventListener('ended', function () {
  changeButton('stop');
}, false); 

参考にしたサイトは以下となります。

まとめると

全体のソースは以下のようになります。今回紹介した部分はコメントをつけてあります。

HTML

<div>
  <form id="audio_form" name="audio_form">
    <fieldset>
      <p>HTML5 Audio Player</p>

      <select name="audio_file" id="audio_file" style="cursor:pointer;">
        <option>♪曲を選択してください♪</option>
        <option value="mm43">MM43</option>
        <option value="mm48">MM48</option>
        <option value="mm53">MM53</option>
        <option value="mm41">MM41</option>
        <option value="mm18">MM18</option>
        <option value="mm9">MM9</option>
      </select>

      <!-- ボタン部分 -->
      <div id="audio_button">  
        <span id="stop" class="button"><i class="icon-step-backward"></i></span>
        <span id="play" class="button"><i class="icon-play"></i></span>
      </div>

      <!-- ボリューム部分 -->
      <div id="audio_volume">
        <i class="icon-volume-down"></i>
        <input type="range" id="volume" value="50" min="0" max="99" />
        <i class="icon-volume-up"></i>
      </div>
    </fieldset>
  </form>
</div>

JavaScript

var audioPlayer = (function () {
  var myAudio = new Audio('');
  var audio_file = document.getElementById('audio_file');  

  function playAudio(myAudio, button) {
    switch(button) {
      case 'play':
        myAudio.play();
        break;
      case 'pause':
        myAudio.pause();
        break;
      case 'stop':
        myAudio.pause();
        myAudio.currentTime = 0;
        break;
    }
  }

  // ボタン切り替え関数を指定
  function changeButton(button) {
    var buttonType = document.getElementsByClassName('button')[1];
    var buttonIcon = buttonType.childNodes[0];

    switch(button) {
      case 'play':
        buttonIcon.setAttribute('class', 'icon-pause');
        buttonType.setAttribute('id', 'pause');
        break;
      case "pause":
        buttonIcon.setAttribute('class', 'icon-play');
        buttonType.setAttribute('id', 'play');
        break;
      default:
        buttonIcon.setAttribute('class', 'icon-play');
        buttonType.setAttribute('id', 'play');
        break;
    }
  }
    
  return {  
    selectAudio: function () {
      audio_file.addEventListener('change', function () {
        var audio_file_name_select = audio_file.selectedIndex;
        var audio_file_name = audio_file[audio_file_name_select].value;

         var canPlayOgg = ('' !== myAudio.canPlayType('audio/ogg'));
         var canPlayMp3 = ('' !== myAudio.canPlayType('audio/mpeg'));

        var ogg = '/wp-content/uploads/music/' + audio_file_name + '.ogg';
        var mp3 = '/wp-content/uploads/music/' + audio_file_name + '.mp3';

        if (canPlayOgg) {
          myAudio.src = ogg;
        } else if(canPlayMp3) {
          myAudio.src = mp3;
        }

        myAudio.play();
        // ボタン切り替え関数を実行
        changeButton('play');

        return myAudio.src;
      }, true);
    },

    clickButton: function () {
      var audio_form_button = document.getElementById('audio_button').childNodes;

      for (var i = 0; i < audio_form_button.length; i++) {  
        audio_form_button[i].addEventListener('click', function () {
          playAudio(myAudio, this.id);
          // ボタン切り替え関数を実行
          changeButton(this.id);      
        }, false);
      }
    },

    // ボリューム切り替え機能
    changeVolume: function () {
      var volume = document.getElementById('volume');

      volume.addEventListener('change', function () {
        var volumeValue = (volume.value.length == 1) ? '0.0' + volume.value : '0.' + volume.value;
        myAudio.volume = volumeValue;
      }, false);
    },

    // 「ended」イベントを取得して、ボタン切り替え関数を実行
    checkEnd: function () {
      myAudio.addEventListener('ended', function () {
        changeButton('stop');  
      }, false); 
    },

    open: function () { 
      // ボリュームの初期値
      myAudio.volume = 0.5;
      this.selectAudio();
      this.clickButton();
      this.changeVolume();
      this.checkEnd();
    }
  };
})();

audioPlayer.open();

CSS

#audio_form {
  padding: 10px 0 0;
  background-color: #666;
  background: linear-gradient(#666 25%, #333 25%, #333 50%, #666 50%, #666 75%, #333 75%, #333);
  background: -webkit-linear-gradient(#666 25%, #333 25%, #333 50%, #666 50%, #666 75%, #333 75%, #333);
  background-size: 3px 3px;
  border-radius: 4px;
  box-shadow: 0 4px 6px #222;
  border: 3px solid #000;
  text-align: center;
  margin: 0 auto 20px;
}
#audio_form fieldset {
  border: none;
  padding: 0;
}
#audio_form p {
  font-size: 20px;
  color: #fff;
  font-weight: bold;
  letter-spacing: 2px;
  text-shadow: 0 2px 2px #000;
  text-align: center !important;
  font-size: 20px;
  margin: 0 auto;
}
#audio_file {
  margin-top: 16px;
  text-align: center;
  width: 80%;
  font-size: 16px;
  letter-spacing: 2px;
  border: 2px inset #000;
  background-color: #f7f7f7;
  border-radius: 4px;
  position: relative;
}

/* ボタン関連ここから */
#audio_button {
  text-align: center;
  margin: 16px auto;
}
.button {
  display: inline-block;
  margin: 0 5px;
  width: 40px;
  line-height: 40px;
  text-align: center;
  background-color: #fff;
  background-image: linear-gradient(-45deg, #e7e7e7 50%, #fff 50%, #fff);
  background-image: -webkit-linear-gradient(-45deg, #e7e7e7 50%, #fff 50%, #fff);
  border-radius: 50%;
  box-shadow: 0 0 3px #222;
  font-size: 16px;
  text-shadow: inset 0 1px 1px #222;
  cursor: pointer;
}
.icon-play {
  padding-left: 4px;
}
/* ボタン関連ここまで */

/* ボリューム関連ここから */
#audio_volume {
  margin-bottom: 10px;
}
.icon-volume-up,
.icon-volume-down,
.icon-volume-off {
  color: #fff;
  text-shadow: 0 1px #222;
  font-size: 20px;
  padding: 0 10px;
}
input[type = "range"] {
  vertical-align: -2px;
  width: 50%;
  cursor: pointer;
}
/* ボリューム関連ここまで */

まとめ

HTML5 Audioを試していますが、いろんな機能が実装ができてなかなか面白いです。今後は余裕があれば、「早送り」や「巻き戻し」機能だったり、「速度」や「ピッチ」の変更機能音楽ファイルのリスト化なんかも試してみようと思っています。

コメント

  1.  vspan じゃなくて <span の筈です

     再生/停止の参考にしに来て、MM48 ばかり聞いてて捗らない

    艦長  返信

    • 艦長さん

      コメントありがとうございます。
      vspanの件、ご指摘ありがとうございました!コピペミスでした…

      MM48気に入っていただけましたでしょうか(笑)
      こちらの曲はもうすでに10年前に作った曲になります。

      HTML5のAudio要素は大変豊富な機能を持っています。
      Web Audio APIなんかと組み合わせて使うと、もっと面白いアプリが作れそうですね!!

      Takanori Maeda  返信

  • 必須

コメント