代码改变世界

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

  muamaker  阅读(1184)  评论(0编辑  收藏  举报

 

在class 组件中,我们需要在  componentDidMounted 里面给 mp3 加上监听,然后在 组件销毁的时候 去掉监听。

来控制 mp3 的播放和暂停。相对来说比较麻烦。难以抽离。

这里用 hooks 达到完全抽离的效果:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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
}

  

 

使用

 

1
2
3
4
5
6
7
8
const TestState = ()=>{
   const [audio,controls,ref] = useAudio({src:"http://cloud.chan3d.com/cdn/website/mp3/1.mp3"})
    return (
        <div className="test-state">
          {audio}
        </div>
    )
}

  

 

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
历史上的今天:
2019-05-13 怎样理解阻塞非阻塞与同步异步的区别? 并行和并发区别?线程与进程
点击右上角即可分享
微信分享提示