前段时间比较闲,顺便就开发了自己的音乐主页👉~~https://music.jw1.dev~~ (已下线),开发过程中遇到不少问题,一度以为解决不了,但是最后看来,其实实现都非常简单。
首先看看 html 吧
<audio src="path/to/your/audio/file"></audio>
<!-- 或者 -->
<audio>
<source src="path/to/your/audio/file" type="audio/mpeg">
</audio>
这时候打开页面你会发现… 什么都没有。
<audio src="path/to/your/audio/file" controls></audio>
加上controls
这个属性,浏览器才会显示原生的音频组件。至于浏览器兼容性我就不讲了,还是老样子扔个 MDN 链接在这里😁:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio#Browser_compatibility
所以,搞定了?
不!像我这种有强迫症的人,怎么可能让每个浏览器的显示都不一致!
要做的第一件事情就是,隐藏audio
的原生控制组件:
<audio src="path/to/your/audio/file"></audio>
<!-- 去掉 controls -->
(👆三遍了🤣
然后用 CSS 将audio
移出文档流:
audio{position:fixed;top:0;left:-12138px;}
为什么要这么做?
—— 为了避免一些非主流的浏览器会让audio
在没有controls
属性的情况下出现在不应该出现的地方。(好累
接下来看 JS 的部分
let audio = document.querySelector('audio')
// 或者用jQuery
let audio = $('audio')
console.log(audio)
原生 JavaScript 在 log 下只能看到标签,而 jQuery 则会把所有和这个元素有关的东西都列出来,不论是有用的还是没有用的。但是知道有那么多属性 / 事件可以用是件好事,在这里说几样比较重要的。
接下来 js 区域中的
audio
都是以原生 js 获取的对象,如果你使用 jQuery,audio 赋值应该为let audio = $($('audio')[0])
audio.play()
可以调用此方法来播放音频。因为市面上大多数浏览器不允许直接用 js 触发音乐播放,必须要由用户来触发该事件 (‘click’, ‘scroll’, ‘focus’, etc.),所以我们可以加一个播放按钮来触发事件:
<audio src="path/to/your/audio/file"></audio>
<button class="play">play</button>
// 获取play button
let playBtn = document.querySelector('.play');
// 给play button添加点击事件
playBtn.addEventListener('click',function(){
// play button 点击后播放音频
audio.play()
})
audio.pause() & audio.paused
调用audio.pause()
来暂停音频。
调用audio.paused
来获取音频暂停状态,true
为暂停状态,false
则为播放状态。
比如我现在需要再点击一次 play button 来暂停音频,我们可以这样做:
playBtn.addEventListener('click',function(){
// 点击后判断音频是否为暂停状态
if(audio.paused){
// 如果audio.paused为true
// 则音频为暂停状态
// 这时候我们就要播放音频
// 同时修改button的文字为pause
audio.play()
playBtn.innerHTML = 'pause'
}else{
// 如果audio.paused为false
// 则音频为播放状态
// 这时候我们就要暂停音频
// 同时修改button的文字为play
audio.pause()
playBtn.innerHTML = 'play'
}
})
audio.currentTime
调用此属性获取音频当前已播放时间。
修改此属性可以达到切换音频当前播放位置的功能:
// 快进十秒
fastForward = function(){
audio.currentTime = audio.currentTime + 10
}
fastForward()
audio.duration
调用此属性获取音频的长度,单位为秒。
已知问题:
- 在
Firefox
上会出现读不到 duration 的情况,可能和修改audio.src
有关,我在自己项目中的做法是使用了ffmpeg
读取出 duration,直接当作变量使用,没有使用浏览器给的这个属性。
audio.ontimeupdate
此方法在音频播放期间会循环调用,经测试,调用间隔大约为 200ms 一次。
此方法可以用在更新音频进度条上。有了上面的audio.currentTime
和audio.duration
,我们可以算出当前音频播放进度的百分比:
// 将百分比定为全局变量
let audioProgressPercent = 0;
audio.ontimeupdate = function(){
// 在audio时间更新的时候计算当前进度百分比
audioProgressPercent = audio.currentTime / audio.duration
console.log(audioProgressPercent)
}
这个时候如果你打开页面开始播放音频,就会看到控制台一直在更新进度百分比了,至于这个数值怎么用,不用我教大家了吧。😁
audio.buffered
调用此属性可以获取当前音频的缓冲进度和区间:
// 每200ms获取一次缓冲区间
let bufferedTimeInterval = setInterval(function(){
// 缓冲可以有多个区间
// 所以我们尽量都获取到
let bufferedLength = audio.buffered.length
// 以bufferedLength做循环
for (let i = 0; i < bufferedLength; i++) {
let bufferedStart = audio.buffered.start(i)
let bufferedEnd = audio.buffered.end(i)
console.log(bufferedStart, bufferedEnd)
}
},200)
控制台输出:
> 0 17.64
56.45 78.90
输出的含义为:
这一次读取audio.buffered
得到了两个区间,所以循环了两次。
第一次获得的区间是[0,17.64]
,代表第 0 秒到第 17.64 秒之间的音频已经缓冲好了,可以直接播放。
第二次获得的区间是[56.45,78.90]
,代表这两个数字之间的音频已经缓冲好了,可以直接播放。
audio.onwaiting & audio.onplay
audio.onwaiting
会在音频开始缓冲的时候被激活;
audio.onplay
会在音频开始播放的时候被激活;
这时候我们可以做一个 loading 的动画,告诉用户音频正在加载了:
audio.onwaiting = function () {
console.log('audio is loading')
// 在这里激活loading动画
}
audio.onplay = function () {
console.log('audio is playing')
// 在这里关闭loading动画
}
audio.ended & audio.src
audio.ended
顾名思义,会在音频结束后被激活;
audio.src
是音频文件的位置,可修改;
当你有一个播放列表,且想在音频结束后播放下一首歌,你就可以:
// 定义播放列表
let playList = [
'path/to/song1',
'path/to/song2',
'path/to/song3'
]
// 定义当前播放歌曲的ID
let currentSongID = 0;
// 音频结束后自动播放下一首
audio.onended = function(){
// 更新当前播放歌曲的ID
currentSongID = currentSongID + 1
// 如果当前播放歌曲为播放列表中最后一首
// 就从第一首开始放
if(currentSongID >= playList.length){
currentSongID = 0
}
// 改变audio的src属性
audio.src = playList[currentSongID]
audio.play()
}
哦还有!
audio.volume
调用获取音频的音量,可修改;
可以做点 fade in /fade out 的效果,使用 jQuery 可以很简单的做出来,原文中也有原生 js 的实现方法 (太长没看
$audio.animate({volume: newVolume}, 1000)
原文链接:https://stackoverflow.com/questions/7451508/html5-audio-playback-with-fade-in-and-fade-out
好了,以上就是我开发音乐主页之后想和大家分享的。
至于题中的问题,我的答案是:真的很简单。
只是 js 有一些性能上的限制,不能什么功能都往网页上搬…
在我的音乐主页中有一个砍掉的功能
—— 节拍器
为啥砍掉了呢…
因为 js 的处理效率真的不高 (或者只是我太辣鸡),中低端手机做出来的节拍器根本不准,至于实现原理,很简单:
歌曲的 bpm (beat per minute) 是已知的:
// 假设现在我有一首歌bpm为120
let bpm = 120
// 每40毫秒更新一次,目的是让bpm更新不低于24fps
// 然而中低端手机的效果却不尽人意
// pc端倒是没啥问题
let bpmTimeInterval = setInterval(function(){
// 计算每秒有多少beats
let interval = bpm / 60
// _b会在时间线呈现出一个锯齿状的图形
// 最高那个点,也就是齿尖,就是一个beat
let _b = audio.currentTime % interval
// ...
},40)
希望有一天 js 能有开发 Digital Audio Workstation 的能力!
✌