▶️
Reactで音声再生バーを作る
概要
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