为了更好的表达高深的东西,仍是须要先强调点简单的东西 web
Audio元素的属性
paused 是否暂停,不能够修改ajax
audio.paused = true // 错误 audio.pause() // 正确
currentTime 当前播放的时间,也能够用来改变进度,如下两行代码是等价的api
audio.currentTime = 10 audio.play(10)
duration 获取当前加载好的音乐的时间数组
loop 是否单曲循魂dom
mute 是否静音oop
volume 音量大小,数值是0 到 1 动画
Audio元素的事件
play和pausethis
播放和暂停的时候触发的事件
seeking和seeked
这里的快进是时间忽然到某一个点上,不是倍速播放,快进开始和快进完成分别触发一个不一样事件,由于快进了可能须要加载数据,须要一点点时间
好比改变currentTime
loadstart,loadeddata和canplay
开始加,加载了一帧数据,加载到足够播放的数据
ended
结束的时候,结束了播放下一首用
还有一些事件,好比progress, volumnchange 这些方法,随便搜一下就能够知道了,你们须要自行搜去,正常状况下这些就够用了。
React播放器
参数
<MusicPlayer getAudio={audio => this.audio = audio} musics={store.musicStore.currentList.songs} />
为了方便启用,核心的参数只有两个
getAudio,和ref同样,能够返回当前的audio元素 musics是一个音乐的列表
由于在audio元素的基础上开发,因此几乎全部的事件其实也都是在audio上经过监听获取到,因此把audio元素抛出能够减小不少配置
传入一个数组 这个数组是一个音乐列表的信息
每一个对象应该包括 id,封面图连接,歌曲连接,歌词连接,歌唱家,歌曲名称
样式
我写的这个播放器是这样的,很简单的样子,基本功能都有,没有循环播放和随机播放的设置,其实也很简单


这个播放器的主要元素包括
封面,歌曲列表,名称,歌词,进度,上一首下一首,还有封面上那个钱币同样的按钮是播放暂停
而后侧面的两个按钮能够控制播放器的显示隐藏和歌曲列表的显示隐藏
具体的UI你们想象着写就行了,反正我写的也不算好看
留出那几个控制的按钮绑定事件就行了
初始化
componentDidMount() { const musics = this.props.musics const audio = this.$audio.current this.setState({ currentIndex: musics && musics.findIndex(item => item.id === window.localStorage.getItem('current-music-id')) || 0, }) audio.addEventListener('play', this.handlePlay) audio.addEventListener('pause', this.handlePause) this.props.getAudio && this.props.getAudio(audio) }
初始化的时候,
1. 添加了事件监听,这样当外部改变audio的状态也会改变播放器组件的状态
2. 获取localstorage里的音乐,这个很友好,能够刷新仍然是那一首歌
3. 抛出audio元素
播放和暂停
handlePlay = () => { const music = this.props.musics[this.state.currentIndex] const audio = this.$audio.current this.handleLoadLrc() this.setState({ paused: false }) window.localStorage.setItem('current-music-id', music.id) audio.play(audio.currentTime) this.timer = setInterval(() => { const { currentTime, duration } = audio this.setState({ currentTime, duration }) }, 100) } handlePause = () => { this.setState({ paused: true }) this.$audio.current.pause() this.timer && clearInterval(this.timer) }
注意
1. currentIndex表示当前的播放的音乐的index
2. 咱们设置了pause的属性 是记录播放器的当前状态,audio的状态改变不能实时反应去渲染dom,因此须要用本身的state记录。
3. play须要传入当前的audio时间,这样暂停播放不会从新开始,而切换后currentTime也会归0
4. 动态记录了currentTime和duration,而且用计时器不对获取,也是由于咱们并无办法实时获取时间反映到进度条上
上一首,下一首和切换
handleNext = () => { let currentIndex = this.state.currentIndex + 1 if (currentIndex >= this.props.musics.length) { currentIndex = 0 } this.setState({currentIndex }, this.handlePlay) } handlePrev = () => { let currentIndex = this.state.currentIndex - 1 if (currentIndex < 0) { currentIndex = this.props.musics.length - 1 } this.setState({currentIndex }, this.handlePlay) } handleToggle = currentIndex => { this.setState({currentIndex }, this.handlePlay) }
上一首就是index - 1而后播放,若是index = 0 那么index为最后一个
下一首就是index +1而后播放,若是index已经最大 那么index重置0
切换就是接受一个index 改变后播放
这三个超级简单,很少解释
快进
handlePlayFrom = e => { const audio = this.$audio.current const { left, width } = e.target.getBoundingClientRect() const clickPos = (e.clientX - left) / width const time = audio.duration * clickPos if (!time) return false audio.currentTime = time }
这个稍微考验些关于dom位置判断的功底
经过获取进度条的宽度,点击的位置,而后计算出一个进度的百分比,最后根据这个百分比和总的音乐时长计算出点击点的时间
加载歌词
handleLoadLrc = () => { const request = new XMLHttpRequest(); const url = this.props.musics[this.state.currentIndex].lrc request.open('GET', url, true); request.onload = () => { this.lyricStr = request.response } request.send(); }
播放的时候回去加载歌词,就是一个ajax请求,返回的歌词通常都是这样的格式

看起来像个数组,实际上是一个字符串,而后咱们能够经过正则将其拆分红一个时间 歌词一一对应的数组,这个是最麻烦的一点,但其实并无什么技术复杂度,主要就是处理字符串经过split拆分红数组就好
而后根据当前的currentTime获取须要显示的那句歌词
不细节分析了,有须要的能够在个人github上看
切换歌单
componentDidUpdate(nextProps) { if (nextProps.musics !== this.props.musics) { this.setState({ currentIndex: 0 }, this.handlePlay) } }
当音乐列表发生改变的时候,从新播放就行了
播放器的显示隐藏
动态添加一个class去隐藏整个播放器就好啦,fixed定位,很容易搞定的
播放器的基本功能就这么多啦,固然还有一些,好比设置播放模式,依次仍是随机,是否单曲循环等等,这些其实都很简单。
具体的代码
戳这里 获取播放器的代码,有须要的自取
总结,在audio元素的基础上开发是比较简单的,可是这也有不少很差的地方,好比说,别人找到audio元素,而后加一个controls属性,就能够下载音乐了。
真正强大的是Audio API,W3C提供了操做音频的一系列api 能够实现更好的音频buffer 播放效果,甚至经过analyser分析音频进行mix混音以及音乐效果的改变。
还能够写好玩的动画效果,好比

这些的前提固然是须要对音乐有必定的了解,有兴趣的小伙伴能够研究一下哦,这是颇有趣的一个方向。