BiTT開発
BiTT開発
2018.12.27
#174
それいけ!フロントエンド

Howler.js+audioSpriteで「BGMのON/OFF切り替え」「BGM切り替え 」「 イコライザーアニメーション」をつくってみよう

はっちゃん

こんにちは、フロントエンドエンジニアのはっちゃんです。

最近はブラウザでさまざまなことが再現できるようになりました。たとえば、WebサイトにBGMを入れたくなるときってありますよね? その場合は、当然サウンドのON/OFFの切り替え機能を作ってユーザーに配慮することが必要になります。

しかし、弊社には「期待を超えろ」というスローガンがあります。そのまま作ることは会社的にも個人的にもプライドが許しません。

そこで今回は、単なるサウンドのオン・オフ切り替え機能にとどまらず、それをさらにパワーアップさせた「サウンド ON/OFF + BGM切り替え + イコライザーアニメーション」を作ってみようと思います。

準備するもの

BGMのコントールに重宝するHowler.js

Howler.jsはさまざまな音楽ファイルのフォーマットを扱うことができ、再生のON/OFF、ストップ、ミュートなどを簡単に実装することができるのでおすすめです。詳しくは下記の公式サイトを参照ください。

公式サイト:https://howlerjs.com/

複数の音楽ファイルをひとまとめにできるaudioSprite

audioSpriteは、複数の音楽ファイルをひとつにまとめ、一度の読み込みで管理操作するテクニック。ffmpegというフリーソフトウェアのラッパーです。

Howler.jsとの互換性もあり、複数の音楽ファイルを管理するときは必須と言っても過言ではないでしょう。

公式サイト:https://github.com/tonistiigi/audiosprite

今回の実装は、事前に今回使用する音楽ファイルをひとまとめにして書き出しておく必要があります。詳しい使い方は以前シスコくんが書いてくれているのでご一読ください。

HTML、CSSコーディング

ではコーディングしていきましょう。

まずはサウンド切り替えスイッチとイコライザー。さくっと見た目だけ作ってしまいます。

HTML

<ul class="sound-select">
  <li>
    <input type="radio" id="sound-select-01" name="sound" data-sound="sound01" class="js-sound-select" checked>
    <label for="sound-select-01">01</label>
  </li>
  <li>
    <input type="radio" id="sound-select-02" name="sound" data-sound="sound02" class="js-sound-select">
    <label for="sound-select-02">02</label>
  </li>
  <li>
    <input type="radio" id="sound-select-03" name="sound" data-sound="sound03" class="js-sound-select">
    <label for="sound-select-03">03</label>
  </li>
</ul>


<div class="equalizer-wrap">
  <input id="equalizer-switch" type="checkbox">
  <label id="js-equalizer" for="equalizer-switch" class="equalizer">
    <span></span>
    <span></span>
    <span></span>
    <span></span>
    <span></span>
  </label>
</div>

CSS

@import url(https://fonts.googleapis.com/css?family=Text+Me+One);

li {
  list-style: none;
}

.sound-select {
  display: flex;
  justify-content: center;
  padding: 85px 0 60px;
  > li {
    padding: 0 40px;
    label {
      display: block;
      font-family: 'Text Me One', sans-serif;
      font-size: 60px;
    }
  }
}

input[type=radio] {
  display: none;
}
.mute-switch {
  padding-left: 30px;
}
.equalizer-wrap {
  margin: 0 auto;
  width: 82px;
}
.equalizer {
  display: block;
  position: relative;
  width: 77px;
  height: 2px;
  padding: 10px 0;
  backface-visibility: hidden;
  span {
    position: absolute;
    top: 50%;
    transform: translateY(-50%) scale(1);
    transform-origin: center;
    height: 2px;
    width: 2px;
    background-color: #000;
    backface-visibility: hidden;
    &:nth-child(1) {
      left: 0;
      height: 8px;
      width: 77px;
      transition: height 0.2s,
      width 0.2s 0.2s;
    }
    &:nth-child(2) {
      height: 25px;
      left: 0;
      animation: equalizer 0.1s linear forwards;
    }
    &:nth-child(3) {
      height: 30px;
      left: 25px;
      animation: equalizer 0.1s linear forwards;
    }
    &:nth-child(4) {
      height: 50px;
      left: 50px;
      animation: equalizer 0.1s linear forwards;
    }
    &:nth-child(5) {
      height: 30px;
      left: 75px;
      animation: equalizer 0.1s linear forwards;
    }
  }
}
input[type=checkbox] {
  display: none;
  &:checked + label {
    width: 77px;
    height: 2px;
    span {
      width: 2px;
      transition: height 0.3s;
      &:nth-child(1) {
        transition: height 0.1s 0.1s,
        width 0.2s;
      }
      &:nth-child(2) {
        transform: translateY(-50%) scaleY(.1);
        animation: equalizer 0.8s linear infinite;
        animation-delay: 0.2s;
      }
      &:nth-child(3) {
        transform: translateY(-50%) scaleY(.15);
        animation: equalizer 0.8s linear infinite;
        animation-delay: 0.4s;
      }
      &:nth-child(4) {
        transform: translateY(-50%) scaleY(.1);
        animation: equalizer 0.8s linear infinite;
        animation-delay: 0.6s;
      }
      &:nth-child(5) {
        transform: translateY(-50%) scaleY(.15);
        animation: equalizer 0.8s linear infinite;
        animation-delay: 0.8s;
      }
    }
  }
}

@keyframes equalizer {
  0% { transform: translateY(-50%) scaleY(1); }
  100% { transform: translateY(-50%) scaleY(.15);}
}

JSコーディング

それではJSで機能を実装していきます。

作る前に、必要なイベント、関数を洗い出しておきましょう。

  • Howler.jsサウンドファイルの読み込み
    • 読み込み直後に実行する処理
  • イベント
    • サウンド切り替え
    • ミュート切り替え

あとはこれにそってコードをガンガン描いていくだけです!

// サウンドファイルの読み込み
let soundId = 'sound01';
let sound = new Howl(
  {
    src: ['https://82mou.github.io/src/sound/blog201809/sound.mp3', 'https://82mou.github.io/src/sound/blog201809/sound.ogg'],
    preload: true,
    autoplay: false,
    loop: true,
    volume: 1,
    sprite: {
      sound01: [0, 64055.98639455782],
      sound02: [66000, 518608.979591],
      sound03: [586000, 196231.836734]
    },
    onload: function () {
      sound.mute(true);
      sound.play(soundId);
    },
    onend: function () {}
  }
);

// イベント
// サウンド切り替え
let clickHandler = function(target) {
  soundId = target;
  sound.pause();
  sound.play(soundId);
}

let soundSelectTargets = document.querySelectorAll('.js-sound-select');
for (let i = 0; i < soundSelectTargets.length; i++) { var soundSelectTarget = soundSelectTargets[i]; soundSelectTarget.addEventListener('click', (e) => {
    clickHandler(e.currentTarget.dataset['sound']);
  }, false);
}

// ミュート切り替え
let equalizerInput = document.querySelector('#equalizer-switch'),
    equalizerLabel = document.querySelector('#js-equalizer');

equalizerLabel.addEventListener('click', changeHandler, false);

function changeHandler() {
  let isChecked = equalizerInput.checked;
  if(isChecked) {
    sound.mute(true);
  } else {
    sound.mute(false);
  }
}

完成

操作方法
サウンド切り替え・・・番号をクリック
ON/OFF切り替え・・・イコライザーをクリック

See the Pen howler.js + equalizer by k_hatsushi (@hatsushi_kazuya) on CodePen.

BGMを思いのままにコントロールできるようになりました。これでBGM付きWebサイト制作はばっちりですね!

まとめ

いかがでしたか? 冒頭でも書いたとおり、最近はブラウザでさまざまなことが再現できるようになり、フロントエンドに求められるものがとっても増えてきています。

ひとつずつしっかり理解して汎用性のあるコードを作り、新しいことにどんどん挑戦していけたらサイコーですね! はっちゃんでした。

LIGにWeb制作について相談してみる!