▶️

Reactで音声再生バーを作る

2024/09/28に公開

概要

Reactで音声再生バーを作ります。
再生ボタンとスライダーが並ぶ形です。

完成図

主に使う機能

コンポーネントの描画にはChakra UIを使用します。

音声系の処理はHTMLAudioElementを扱います。

  • audio.currentTime:音声ファイルの再生位置(s)
  • audio.play():音声ファイルを再生
  • audio.pause():音声ファイルを停止
  • endedイベントリスナー:音声ファイルの終了を検知する
  • timeupdateイベントリスナー:音声ファイルの再生位置の更新を検知する

コード

Audio要素、音声再生中かどうか、再生時間をstateで管理します。

 const [audio] = useState(new Audio(/** 音声ファイルのパス */)

  /** 音声再生状況をstate管理 */
  const [audioPlaying, setAudioPlaying] = useState(false);

  /** 音声の再生時間(s)をstate管理 */
  const [audioCurrentTime, setAudioCurrentTime] = useState(0);

audioPlayingに基づいて、useEffectでAudio要素の再生・停止状況を操作します。
再生が終了したら、audioPlayingをfalseにするのも忘れずに。

  /** 再生・停止の更新 */
  useEffect(() => {
    if (audioPlaying) {
      audio.play().catch(() => {
        throw new Error("音声ファイルが見つかりません");
      });
    } else {
      audio.pause();
    }
    return () => {
      audio.pause();
    };
  }, [audio, audioPlaying]);

  /** 再生終了時の処理 */
  useEffect(() => {
    const onEnded = () => {
      setAudioPlaying(false);
      audio.pause();
    };

    audio.addEventListener("ended", onEnded);
    return () => {
      audio.removeEventListener("ended", onEnded);
    };
  }, [audio, setAudioPlaying]);

音声の再生時間を、timeupdateイベントリスナーで管理します。

  /** 音声の再生時間を更新する処理 */
  useEffect(() => {
    const onTimeupdate = () => {
      setAudioCurrentTime(audio.currentTime);
    };

    audio.addEventListener("timeupdate", onTimeupdate);
    return () => {
      audio.removeEventListener("timeupdate", onTimeupdate);
    };
  }, [audio, setAudioCurrentTime]);

わかりやすいように、「00:00」という時刻で表記します。
date-fnsのsecondsToMinutesなどが使えます。

  /** 秒数を「00:00」の形式に変換する処理 */
  const getTimeStringFromSeconds = (seconds: number): string => {
    const floorSeconds = Math.floor(seconds);
    const mm = String(secondsToMinutes(floorSeconds) % 60).padStart(2, "0");
    const ss = String(floorSeconds % 60).padStart(2, "0");
    return `${mm}:${ss}`;
  };

続いて、再生ボタンコンポーネントを作成します。

<Circle
    as={'button'}
    bg={'black'}
    onClick={() => {
      setAudioPlaying((prev) => !prev);
    }}
    size={'37px'}
  >
    <Icon
      as={
        audioPlaying ? /** 停止アイコン */ : /** 再生アイコン */
      }
      color={'white'}
      fontSize={'24px'}
    />
  </Circle>

アイコン用にはReact Iconsを使用すると便利です。

スライダーコンポーネント部分の記述にはChakra UIのSliderを使用します。
スライダーのminとmaxを設定でき、スライドした位置の値をvalueに設定することができるので非常に便利です。

スライダーマークの上に再生時間を表記してみます。

<Slider
  max={audio.duration}
  min={0}
  onChange={audioJump}
  value={audioCurrentTime}
>
  <SliderTrack>
    <SliderFilledTrack />
  </SliderTrack>
  <SliderMark
    fontSize={"11px"}
    fontWeight={"bold"}
    textAlign={"center"}
    transform={"translateX(-50%)"}
    mt={2}
    value={audioCurrentTime}
  >
    {getTimeStringFromSeconds(audioCurrentTime)}
  </SliderMark>
  <SliderThumb />
</Slider>;

スライダーが設置された時間にジャンプさせる処理も追加します。

  /** 〇〇秒にジャンプ */
  const audioJump = (targetSecond: number) => {
    if (!isNaN(targetSecond) && isFinite(targetSecond)) {
      audio.currentTime = targetSecond;
    }
  };

これで音声再生バーができました!

さらに、SliderのonChangeStartやonChangeEndを使えば、スライド中には音声を停止するなどの複雑な処理も可能です。

自由にカスタマイズしてオリジナル音声再生バーを作りましょう!

Discussion