24.06.2024 в 07:42
Алексей Устинов
Как воспроизводить аудио с помощью React
В этой статье я покажу вам, как я воспроизвожу аудио в интерфейсах React.
Настройка звука
Где-то на S3 у меня есть 4 аудиофайла, распространяемые CloudFront:
const musics = [
'https://alterego.community/audio/classique.mp4',
'https://alterego.community/audio/folk.mp4',
'https://alterego.community/audio/electro.mp4',
'https://alterego.community/audio/hip-hop.mp4'
];
Для каждой музыки я создаю аудиоэлемент и загружаю его при монтировании:
const [audio, setAudio] = useState<HTMLAudioElement>();
useEffect(() => {
const _audio = new Audio(musics[props.num - 1]);
_audio.load();
_audio.addEventListener('canplaythrough', () => {
setAudio(_audio);
});
}, []);
Прослушивание взаимодействия пользователя
Я добавляю ref
к родительскому элементу анимации Lottie, чтобы прослушивать события наведения и касания:
const musicRef = useRef<HTMLDivElement>(null);
<$Music ref={musicRef}>
<Lottie hover loop={true} src={musicAnimation} />
</$Music>
Теперь, когда звук готов, я могу прослушивать взаимодействие пользователя:
useEffect(() => {
if (!audio) return;
if (!musicRef) return;
const play = async () => {
audio.play();
};
const stop = () => {
audio.pause();
audio.currentTime = 0;
};
const element = musicRef.current;
if (element) {
element.addEventListener('mouseenter', play);
element.addEventListener('mouseleave', stop);
element.addEventListener('touchstart', play);
element.addEventListener('touchend', stop);
return () => {
element.removeEventListener('mouseenter', play);
element.removeEventListener('mouseleave', stop);
element.removeEventListener('touchstart', play);
element.removeEventListener('touchend', stop);
};
}
}, [musicRef, audio]);
Подключаем состояние
Нам нужно сохранить выбранную музыку в состоянии:
export const PICK_MUSIC = '@@user/PICK_MUSIC';
type PickMusicPayload = {musicChoice: MusicChoice};
export type PickMusicAction = {
type: '@@user/PICK_MUSIC';
payload: PickMusicPayload;
};
const onPickMusic = {
on: PICK_MUSIC,
reduce: (state: State, payload: PickMusicPayload) => {
const {musicChoice} = payload;
state.preferredMusic = musicChoice;
}
};
Затем часть React:
import {useTaverne} from 'taverne/hooks';
// ... in the component:
const {dispatch, pour} = useTaverne();
const preferredMusic = (pour('user.preferredMusic') || -1) as MusicChoice;
const pickMusic = (musicChoice: MusicChoice) => () => {
dispatch({
type: PICK_MUSIC,
payload: {musicChoice}
} as PickMusicAction);
};
<Music
num={musicNum}
selected={preferredMusic === musicNum}
onClick={pickMusic(musicNum)}
/>
Монтируем все это вместе
Родительский компонент, отображающий всю музыку:
type MusicChoice = -1 | 1 | 2 | 3 | 4;
const musicChoices: Array<MusicChoice> = [1, 2, 3, 4];
<$Musics>
{musicChoices.map(musicNum => (
<Music
key={`music-${musicNum}`}
num={musicNum}
selected={preferredMusic === musicNum}
onClick={pickMusic(musicNum)}
/>
))}
</$Musics>