I want to create an audioplayer on React Nextjs, with some dots, each one corresponding to an audio track. When I click on a dot, it pause the track that currently is playing, if there is one, and play the new one instead.
I have some playing problems. When I arrive on the page and click on a dots, I need to click it 3 times to play the track. If I click on a new one, I need to click 2 times to play the new one.
Here is the code :
AudioPlayer.js
import React, { useState, useRef, useEffect } from 'react';
const AudioPlayer = () => {
// state
const [isPlaying, setIsPLaying] = useState(false);
const [duration, setDuration] = useState(0);
const [currentTime, setCurrentTime] = useState(0);
const [currentTrack, setCurrentTrack] = useState(null)
// references
const audioPlayer = useRef(); //reference to our audioplayer
const progressBar = useRef(); //reference to our progressBar
const animationRef = useRef() //reference the animation
useEffect(() => {
const seconds = Math.floor(audioPlayer.current.duration)
setDuration(seconds);
progressBar.current.max = seconds;
}, [audioPlayer?.current?.loadmetadata, audioPlayer?.current?.readyState])
const togglePlayPause = () => {
const prevValue = isPlaying;
setIsPLaying(!prevValue);
if (!prevValue) {
audioPlayer.current.play();
animationRef.current = requestAnimationFrame(whilePlaying);
} else {
audioPlayer.current.pause();
cancelAnimationFrame(animationRef.current);
}
}
const whilePlaying = () => {
progressBar.current.value = audioPlayer.current.currentTime;
setCurrentTime(progressBar.current.value);
animationRef.current = requestAnimationFrame(whilePlaying);
}
const calculateTime = (secs) => {
const minutes = Math.floor(secs / 60);
const returnedMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
const seconds = Math.floor(secs % 60);
const returnedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`;
return `${returnedMinutes} : ${returnedSeconds}`
}
const changeRange = () => {
audioPlayer.current.currentTime = progressBar.current.value;
setCurrentTime(progressBar.current.value);
}
const changeTrack = (e) => {
if (isPlaying) {}
setCurrentTrack(e.target.value)
console.log(e.target.value)
togglePlayPause();
}
return (
<>
<input ref={audioPlayer} className='dots' value='/piste1.mp3' onClick={e => changeTrack(e)}></input>
<input ref={audioPlayer} className='dots left-8' value='/piste2.mp3' onClick={e => changeTrack(e)}></input>
<div>
<audio ref={audioPlayer} src={currentTrack} preload='metadata'></audio>
<button className="mx-5" onClick={togglePlayPause}>
{isPlaying ? "Pause" : "Play"}
</button>
{/* Current time */}
<div>{calculateTime(currentTime)}</div>
{/* progress bar */}
<div>
<input type='range' defaultValue="0" ref={progressBar} onChange={changeRange} />
</div>
{/* duration */}
<div>{(duration && !isNaN(duration)) && calculateTime(duration)}</div>
</div>
</>
)
}
export default AudioPlayer
CodePudding user response:
Why is the audioPlayer
being passed as a ref to 3 different elements. That looks like it will produce issues. Only your audio element should have audioPlayer
as a ref.
No need for togglePlayPause
function in changeTrack
. That code looks like it's causing quite some issues as well. Anyways, your chanfeTrack
code should look like this:
const changeTrack = (evt) => {
setCurrentTrack(evt.target.value)
};
If the audio doesn't immediately start playing, make sure the onCanPlay
event is set to play the audio using something like evt.target.play()