• 【小程序】实现一个定制的音乐播放器


    8a941a1295752c7ba2966d4a38f83839.png

    应用地址:https://spacexcode.com/player

    介绍

    这是为自己制作的一个在线 Web 版的音乐播放器。众所周知,现在市面上的所有的音乐平台都是会员制。而免费的资源却分散在网络上的各个角落,为此,我收集了自己 喜欢的音乐,放到自己的服务器上,并制作了这样一个专属歌单的简单的音乐播放器。

    实现讲解

    该播放器虽然简单,但也是五脏俱全,该有的功能一个都没少。比如常见的音乐播放器,我们能想到的功能有:

    • 暂停/开始播放按键

    • 调节播放进度的进度条

    • 调节声音大小的按键

    • 切换下一首/上一首按键

    • 查看歌单的界面

    界面

    如果不考虑界面, Web 端的音频播放有现成的标签 audio 支持

    "https://spacexcode.oss-cn-hangzhou.aliyuncs.com/mp3/那女孩对我说.mp3" />
    8e382cf0d9905149be535d735f71b5da.png

    这是浏览器渲染的默认样式,各个浏览器可能会有差异。我们要实现一个音乐播放器,当然要所有端的样式统一。这个时候,我们就移除 controls 属性,让 实际的播放组件在页面上什么都不显示。然后我们自己去实现上面的所有控件。

    由于这个站点引入的是 Material UI,为了统一视觉,就使用里面现成的组件去实现。

    1. function player () {
    2.   const [paused, setPaused] = React.useState(true);
    3.   return (
    4.     '100%', overflow: 'hidden' }}>
    5.       
    6.         padding: 16,
    7.         borderRadius: 16,
    8.         width: 343,
    9.         maxWidth: '100%',
    10.         margin: 'auto',
    11.         position: 'relative',
    12.         zIndex: 1,
    13.         backgroundColor: 'rgba(255,255,255,0.4)',
    14.         backdropFilter: 'blur(40px)'
    15.       }}>
    16.         'flex', alignItems: 'center', position: 'relative' }}>
    17.           
    18.             width: 100,
    19.             height: 100,
    20.             objectFit: 'cover',
    21.             overflow: 'hidden',
    22.             flexShrink: 0,
    23.             borderRadius: 8,
    24.             backgroundColor: 'rgba(0,0,0,0.08)',
    25.             '& > img': {
    26.               width: '100%',
    27.             }
    28.           }}>
    29.             '那女孩对我说' src='https://spacexcode.oss-cn-hangzhou.aliyuncs.com/1697270523238-8b4b11a5-b5a3-4ac3-b6bd-1e264f526c76.png' />
    30.           
  •           'absolute', top: 0, right: 0 }}>
  •             "music queue">
  •               "small" htmlColor='rgba(0,0,0,0.4)' />
  •             
  •           
  •           1.5, minWidth: 0 }}>
  •             "caption" color="text.secondary" fontWeight={500}> 林俊杰 
  •              那女孩对我说 
  •             -0.25}> 心很空 天很大 云很重 我很孤单 
  •           
  •         
  •         "time-indicator" size="small" value={30} min={0} step={1} max={405}
  •           sx={{
  •             color: 'rgba(0,0,0,0.87)',
  •             height: 4,
  •             '& .MuiSlider-thumb': {
  •               width: 8,
  •               height: 8,
  •               transition: '0.3s cubic-bezier(.47,1.64,.41,.8)',
  •               '&:before': {
  •                 boxShadow: '0 2px 12px 0 rgba(0,0,0,0.4)',
  •               },
  •               '&:hover, &.Mui-focusVisible': {
  •                 boxShadow: `0px 0px 0px 8px ${
  •                   'rgb(0 0 0 / 16%)'
  •                 }`,
  •               },
  •               '&.Mui-active': {
  •                 width: 20,
  •                 height: 20,
  •               },
  •             },
  •             '& .MuiSlider-rail': {
  •               opacity: 0.28,
  •             },
  •           }}
  •         />
  •         'flex', alignItems: 'center', justifyContent: 'space-between', mt: -2 }}
  •         >
  •           '0.75rem', opacity: 0.38, fontWeight: 500, letterSpacing: 0.2 }}>00:30
  •           '0.75rem', opacity: 0.38, fontWeight: 500, letterSpacing: 0.2 }}>-04:00
  •         
  •         'flex', alignItems: 'center', justifyContent: 'center', mt: -1 }}
  •         >
  •           'https://spacexcode.oss-cn-hangzhou.aliyuncs.com/mp3/那女孩对我说.mp3' />
  •           "previous song">
  •             "large" htmlColor='#000' />
  •           
  •           'pause'>
  •             '3rem' }} htmlColor='#000' />
  •           
  •           "next song">
  •             "large" htmlColor='#000' />
  •           
  •         
  •         2} direction="row" sx={{ mb: 1, px: 1 }} alignItems="center">
  •           'rgba(0,0,0,0.4)' />
  •           "Volume" value={0.3} min={0} step={0.01} max={1}
  •             sx={{
  •               color: 'rgba(0,0,0,0.87)',
  •               '& .MuiSlider-track': {
  •                 border: 'none',
  •               },
  •               '& .MuiSlider-thumb': {
  •                 width: 16,
  •                 height: 16,
  •                 backgroundColor: '#fff',
  •                 '&:before': {
  •                   boxShadow: '0 4px 8px rgba(0,0,0,0.4)',
  •                 },
  •                 '&:hover, &.Mui-focusVisible, &.Mui-active': {
  •                   boxShadow: 'none',
  •                 },
  •               },
  •             }}
  •           />
  •           'rgba(0,0,0,0.4)' />
  •         
  •       
  •     
  •   )
  • }
  • 631352b68166a0eac2ce60cf0275accc.png

    至此以上代码,初步实现了基本的播放器界面,然后我们需要对每个功能控件添加事件实现它应该有的功能。

    播放按钮

    控制播放器的播放,我们可以通过 audio 提供的 play()pause() 接口,首先我们使用 useRef 获取音频组件的实例,然后通过判断 当前的播放状态,调用播放和暂停接口。

    1. function Player () {
    2.   const audioPlayer = useRef();
    3.   const [paused, setPaused] = useState(true);
    4.   const onPlayOrPause = () => {
    5.     paused ? audioPlayer.current.play() : audioPlayer.current.pause();
    6.     setPaused(!paused)
    7.   }
    8. }

    下面所有使用的变量 audioPlayer 代表获取的 播放器实例

    调节进度

    Slider 组件上绑定 onChange 事件,此时 Slider 组件中的最大值即为该音频资源的最大时长。回调中拿到设置的进度值后赋值给 currentTime 属性。

    1. const [position, setPosition] = useState(0); // 表示当前的播放进度值
    2. const onChangeProgress = (val) => {
    3.   setPosition(val);
    4.   audioPlayer.current.currentTime = val;
    5. }

    这个音频的资源时长,可以通过监听资源加载完成事件,通过 duration 属性获取。它单位为**毫秒(ms)**。

    1. audioPlayer.current.addEventListener('loadeddata', () => {
    2.   setDuration(audioPlayer.current?.duration)
    3. });

    调节音量

    音量的调节和进度同样使用的是 Slider 组件,不过它的最大值为 1,步长为 0.01

    改变音量时,将获取到的音量值赋给 audioPlayer 实例的 volumn 属性。

    1. const onChangeVolume = (val) => {
    2.   setVolume(val);
    3.   audioPlayer.current.volume = val;
    4. };

    切换歌单

    切换上一首/下一首,无非就是改变当前播放的音频资源。该音频资源对应到资源列表的索引值,我们通过控制索引值去列表中获取不同的音乐素材。

    这里要考虑边界值的情况,当切换的时候碰到最大值和最小值,如果允许循环,那么在索引值等于数组长度时,将它的值重置为 0

    1. // 上一首
    2. const onPreview = () => {
    3.   if (currentIndex > 0) {
    4.     changeCurrentIndex(currentIndex - 1); 
    5.   }
    6. }
    7. // 下一首
    8. const onNext = () => {
    9.   if (currentIndex < songList.length - 1) {
    10.     changeCurrentIndex(currentIndex + 1);
    11.   } else {
    12.     changeCurrentIndex(0);
    13.   }
    14. }

    查看歌单

    除了所有的功能按钮,右上角还有一个查看歌单的按钮,点击从底部弹出一个 drawer 抽屉组件。里面列出了一个收藏的音乐列表。

    1. function MusicQueue() {
    2.   const songList = [
    3.     {
    4.       artists: '林俊杰',
    5.       name: '那女孩对我说',
    6.       avatar: 'https://spacexcode.oss-cn-hangzhou.aliyuncs.com/1697270523238-8b4b11a5-b5a3-4ac3-b6bd-1e264f526c76.png',
    7.       link: 'https://spacexcode.oss-cn-hangzhou.aliyuncs.com/mp3/那女孩对我说.mp3',
    8.       lyric: '心很空 天很大 云很重 我很孤单'
    9.     },
    10.     {
    11.       artists: '张靓颖',
    12.       name: '终于等到你',
    13.       avatar: 'https://spacexcode.oss-cn-hangzhou.aliyuncs.com/1697503257045-7a262b7d-0df0-4005-a69c-2a645ac24c27.png',
    14.       link: 'https://spacexcode.oss-cn-hangzhou.aliyuncs.com/mp3/终于等到你-张靓颖.mp3',
    15.       lyric: '到了某个年纪你就会知道 一个人的日子真的难熬'
    16.     },
    17.     {
    18.       artists: '张靓颖',
    19.       name: '饿狼传说',
    20.       avatar: 'https://spacexcode.oss-cn-hangzhou.aliyuncs.com/1697503257045-7a262b7d-0df0-4005-a69c-2a645ac24c27.png',
    21.       link: 'https://spacexcode.oss-cn-hangzhou.aliyuncs.com/mp3/饿狼传说-张靓颖.mp3',
    22.       lyric: '她熄掉晚灯 幽幽掩两肩 交织了火花 拘禁在沉淀'
    23.     }
    24.   ];
    25.   return (
    26.     'auto' }} role="presentation">
    27.       
    28.         {songList.map((item, index) => (
    29.           
    30.             key={index} 
    31.             disablePadding>
    32.             
    33.               
    34.                 
    35.               
    36.               
    37.                 
    38.                   'inline' }} component="span" variant="body1" color="text.primary">
    39.                     {item.name}
    40.                   
    41.                   {' -- ' + item.artists}
    42.                 
    43.               } />
    44.             
    45.           
    46.         ))}
    47.       
    48.     
    49.   );
    50. }
    ab4da5ca5227a003db10c1bc4a386227.png

    至此我们已经实现了界面上所有的控制按钮的功能。最后需要完善下一些边界和初始情况的处理。

    当音频在播放的时候,进度条需要随着当前播放时间变化而变化。这里采用定时器,每隔一秒获取播放资源的当前播放时间点,同步给 Slider 组件的 value 属性来定位。

    1. const [timer, setTimer] = useState(null);
    2. audioPlayer.current.addEventListener('play', () => {
    3.   if (timer) {
    4.     clearInterval(timer);
    5.   }
    6.   setTimer(setInterval(() => {
    7.     setPosition(audioPlayer.current?.currentTime)
    8.   }, 1000));
    9. });
    10. audioPlayer.current.addEventListener('pause', () => {
    11.   if (timer) {
    12.     clearInterval(timer);
    13.   }
    14. });

    还有歌词的显示,下一步计划加上~

    总结

    实现一个定制的音乐播放器不复杂,控制功能的实现都有现成的接口:

    我们需要考虑的是如何优化界面,毕竟一款颜值高的播放器和动听的音乐才能给我们带来内心的愉悦。

    - END -
  • 相关阅读:
    【Kingbase FlySync】评估工具安装及使用
    Ae 效果:CC Overbrights
    2022年《微信小程序从基础到uni-app项目实战》
    【云原生】nacos权限制认证
    跟我学c++初级篇——汇编语言的使用
    金九银十面试跳槽季:且看程序员如何秒变 offer 收割小能手
    【无标题】
    星图地球数据云,便捷加载各类在线地图服务的又一神器
    QT 数据库表格----QSqlTableModel
    SpringBoot:如何优雅地进行响应数据封装、异常处理?
  • 原文地址:https://blog.csdn.net/u013919171/article/details/133956631