在class 組件中,我們需要在 componentDidMounted 里面給 mp3 加上監聽,然后在 組件銷毀的時候 去掉監聽。
來控制 mp3 的播放和暫停。相對來說比較麻煩。難以抽離。
這里用 hooks 達到完全抽離的效果:
interface IAudioProps extends React.AudioHTMLAttributes<any> {
src: string
}
const wrapEvent = (userEvent:any, proxyEvent?:any) => {
return (event:any) => {
try {
proxyEvent && proxyEvent(event);
} finally {
userEvent && userEvent(event);
}
};
};
const useAudio = (props:IAudioProps)=>{
const ref = useRef< HTMLAudioElement | null >(null)
const [state,setState] = useState({
time: 0,
duration: 0,
paused: true,
muted: false,
volume: 1
});
const onPlay = ()=>{
setState((obj)=>{ return {...obj,paused:false} })
}
const onPause = ()=>{
setState((obj)=>{ return {...obj,paused:true} })
}
const element = React.createElement("audio",{
...props,
ref,
onPlay: wrapEvent(props.onPlay, onPlay),
onPause: wrapEvent(props.onPause, onPause),
onEnded: wrapEvent(props.onEnded, onPause),
})
let lockPlay: boolean = false;
const controls = {
play: () => {
const el = ref.current;
if (!el) {
return undefined;
}
if (!lockPlay) {
const promise = el.play();
const isPromise = typeof promise === 'object';
if (isPromise) {
lockPlay = true;
const resetLock = () => {
lockPlay = false;
};
promise.then(resetLock, resetLock);
}
return promise;
}
return undefined;
},
pause: () => {
const el = ref.current;
if (el && !lockPlay) {
return el.pause();
}
},
seek: (time: number) => {
const el = ref.current;
if (!el || state.duration === undefined) {
return;
}
time = Math.min(state.duration, Math.max(0, time));
el.currentTime = time;
},
volume: (volume: number) => {
const el = ref.current;
if (!el) {
return;
}
volume = Math.min(1, Math.max(0, volume));
el.volume = volume;
setState((obj)=>{ return {...obj,volume} });
},
mute: () => {
const el = ref.current;
if (!el) {
return;
}
el.muted = true;
},
unmute: () => {
const el = ref.current;
if (!el) {
return;
}
el.muted = false;
},
};
return [
<span>
{element}
{
state.paused ? <button onClick={controls.play}>點擊播放</button>:<button onClick={controls.pause}>點擊暫停</button>
}
</span>,
controls,
ref
] as const
}
使用
const TestState = ()=>{
const [audio,controls,ref] = useAudio({src:"http://cloud.chan3d.com/cdn/website/mp3/1.mp3"})
return (
<div className="test-state">
{audio}
</div>
)
}
