代码改变世界

使用 React hooks 优雅解决 mp3 的播放 和 暂停

2021-05-13 12:12  muamaker  阅读(1154)  评论(0编辑  收藏  举报

 

在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>
    )
}