ゆったりWeb手帳

気になったことをのんびり書くよ

🎼

audioファイルの読み込み量を表示する

audioファイルの読み込み量を取得する

[前回](../audio-seekbar)、シークバーを追加する方法を紹介しました。
今回は、そのシークバーでオーディオファイルの読み込み状況がわかるようにします。

読み込み量を表示

Webでオーディオを扱うときは、使うmp3ファイルは必ずしもローカルにあるわけではなく、同じくWebから取得すると思います。
当然ダウンロードには時間がかかるので、再生ボタンを押してから無反応になってしまうとUXは低下してしまいます。
読み込み量はonprogressイベントが発生しているときにbuffered属性からTimeRangesオブジェクト形式として取得できます。

HTMLの追加

新しく#loading要素を追加します。
HTML
<div id="loading"></div>

CSSの追加

#loading#seekbarとCSSを共有しています。
参考: [前回](../audio-seekbar)
CSS
#seekbar, #loading {
  width: 160px;
  height: 10px;
  margin: 1em;
  border-radius: 5px;
  background: linear-gradient(#ccc, #ccc) no-repeat #eee;
}
post-image
こんな感じ。
background-colorを透過させてシークバーと読み込みプログレスバーを重ねてもいいですね。

JavaScriptの追加

audio.onprogressに直接追加します。
JavaScript
audio.onprogress = function () {
  if(audio.buffered.length > 0){
    const percent = Math.round((audio.buffered.end(audio.buffered.length-1)/audio.duration)*1000)/10
    document.getElementById('loading').style.backgroundSize = percent + '%'
  }
}
これで読み込みの状況も確認できるようになりました。
post-image
<audio>タグはストリーミング再生なので、完全にダウンロードされてから再生されるのではなく、再生速度をみながら、ここまで読み込めばあとは再生中にダウンロードできると判断した時点から再生開始されます。
そのへんはブラウザがいい感じにやってくれます。
ちなみにそのときはoncanplaythroughなどのイベントが発火します。

まとめ

最後にここまでをひとつにまとめたものです。
長くなってきちゃいましたね。
index.html
<html>
<head>
  <title>HTML5のAudioタグテスト</title>
  <style>
    audio {
      display: none
    }
    #play, #stop {
      display: inline-block;
      width: 60px;
      height: 60px;
      margin: 1em;
      border-radius: 50%;
      background: #ddd;
      text-align: center;
      line-height: 60px;
      cursor: pointer;
    }
    #play:hover, #stop:hover {
      background: #eee;
    }
    #time {
      margin: 0 1em;
    }
    #current, #duration {
      padding: 0;
      margin: .2em;
    }
    #seekbar, #loading {
      width: 160px;
      height: 10px;
      margin: 1em;
      border-radius: 5px;
      background: linear-gradient(#ccc, #ccc) no-repeat #eee;
    }
  </style>
</head>
<body>
  <div>
    <audio src="sample.mp3"></audio>
    <div id="play">Play</div>
    <div id="stop">Stop</div>
    <div id="time">
      <span id="current">00:00</span>
      <span id="duration">00:00</span>
    </div>
    <div id="loading"></div>
    <div id="seekbar"></div>
  </div>
  <script>
    const audio = document.getElementsByTagName("audio")[0]
    const playButton = document.getElementById("play")
    const stopButton = document.getElementById("stop")
    playButton.addEventListener('click', () => {
      if (audio.paused) {
        audio.play()
        play.innerHTML = play.innerHTML === 'Play' ? 'Pause' : 'Play'
      } else {
        audio.pause()
        play.innerHTML = 'Play'
      }
    })
    stopButton.addEventListener('click', () => {
      audio.pause()
      audio.currentTime = 0
    })
    audio.onprogress = function () {
      if(audio.buffered.length > 0){
        const percent = Math.round((audio.buffered.end(audio.buffered.length-1)/audio.duration)*1000)/10
        document.getElementById('loading').style.backgroundSize = percent + '%'
      }
    }
    audio.addEventListener("timeupdate", (e) => {
      const current = Math.floor(audio.currentTime)
      const duration = Math.round(audio.duration)
      if(!isNaN(duration)){
        document.getElementById('current').innerHTML = playTime(current)
        document.getElementById('duration').innerHTML = playTime(duration)
        const percent = Math.round((audio.currentTime/audio.duration)*1000)/10
        document.getElementById('seekbar').style.backgroundSize = percent + '%'
      }
    })
    document.getElementById('seekbar').addEventListener("click", (e) => {
      const duration = Math.round(audio.duration)
      if(!isNaN(duration)){
        const mouse = e.pageX
        const element = document.getElementById('seekbar')
        const rect = element.getBoundingClientRect()
        const position = rect.left + window.pageXOffset
        const offset = mouse - position
        const width = rect.right - rect.left
        audio.currentTime = Math.round(duration * (offset / width))
      }
    })
    function playTime (t) {
      let hms = ''
      const h = t / 3600 | 0
      const m = t % 3600 / 60 | 0
      const s = t % 60
      const z2 = (v) => {
        const s = '00' + v
        return s.substr(s.length - 2, 2)
      }
      if(h != 0){
        hms = h + ':' + z2(m) + ':' + z2(s)
      }else if(m != 0){
        hms = z2(m) + ':' + z2(s)
      }else{
        hms = '00:' + z2(s)
      }
      return hms
    }
  </script>
</body>
</html>
とりあえずオーディオプレイヤーを作るシリーズはこれでいったん終わりにします。
HTMLMediaElementの属性やメソッドはMDNのまとめが参考になります。
参考: HTMLMediaElement | MDN
なにかご意見などありましたらTwitterまでお願いいたします。
次の記事Reactを始めてみる