包括音频文件的展示、播放、暂停、切换、自定义的进度条、手动点击和滑动进度条切换音频播放进度等效果的实现。
Audio嵌入音频
通过元素,我们可以在文档中嵌入音频内容。
有两种方式引入音频文件,一种是通过audio的src属性,一种就是通过子元素,如下两个例子。
<audio src={audioUrl}>
你的浏览器不支持audio标签
</audio>
<audio>
<source src={audioUrl} />
<source src={audioUrl2} />
</audio>
我更建议你使用元素,因为src只可以提供一个播放源,当这个文件的格式不被浏览器支持的时候,就无法正常播放。通过可以引入多条不同格式音频文件,这时候浏览器会使用第一个它支持的播放源。
音频文件的播放与常用方法
<audio
preload="auto"
id="audio-area"
onCanPlay={canPlay}
onError={onError}
onPlaying={onPlaying}
onPause={onPause}
onEnded={onEnded}
>
<source src={audioUrl} />
</audio>
audio的属性controls,可以展示浏览器默认的控制组件,包括播放、暂停、音量、进度条。如果产品不介意样式,可以直接使用,也就不用关注接下来的内容。
如果产品需要自定义播放界面,特别是在c端,不会使用浏览器音频播放的默认样式,而是需要前端自己写,包括进度条,这个时候就需要使用audio的属性和方法了。
下面介绍几个常用的属性和方法:
属性:
onCanplay: 当音频文件下载完毕后就会调用这个方法,这个时候可以获得音频的时长。
**onPlaying:当play的时候会监听到这个事件,**表示音频准备开始播放了,这个时候可以开始获取文件的播放时长并更新进度条。
**onPause:**当pause的时候会监听到这个事件,表示音频暂停。
**onEnded: ** 表示一段音频播放完毕,通常我们在一段音频播放完毕后开始播放下一个音频的操作,通常在这个方法中实现。
duration: 当前音频的总时长
const audioNode = document.getElementById('audio-area');
audioNode.duration;
currentTime: 通常用来获取和设置当前文件已经播放的进度
audioNode.currentTime
方法:
**play: **手动播放文件。如需音频加载完毕后立即播放,通常将这个方法放在canPlay中。
audioNode.play();
audioNode.play().then(() => {
...
}).catch((e) => {
console.log('canPlay error:', e);
});
当音频没有正常播放时,可以在catch中获得报错信息。
pause: 手动暂停,可以将一个正在播放中的文件暂停,下次play的时候,将会接在当前播放记录之后继续播放。
audioNode.pause();
load: 修改音频文件路径后,如果需要播放新的音频,需要手动调用load方法。
const audioNode = document.getElementById('audio-area');
audioNode.load();
音频的切换
如果有一个音频列表,需要从当前音频切换到下一个,通常会怎么做呢。
const audioNode = document.getElementById('audio-area') ;
audioNode.pause();
- 修改audio的文件路径,如果你使用的是react,只需要修改state中绑定的值就可以
3)调用load重新加载文件
audioNode.load();
4)这个时候如果事先在canPlay中写了自动play的话,就无需其他操作。音频加载完毕,就会自动播放
五、自定义进度条(进度计算、点击和滑动实现音频进度的更新)
实现自定义的进度条。我这边的方案是通过currentTime和duration来实现。
const progressInfo = currentAudio.audio.duration && currentTime ?
Math.round((currentTime / currentAudio.audio.duration) * 100) : 0;
进度条:
<section className={styles.progress} onTouchMove={onTouchMove} onMouseDown={changeDuration} >
<div id="progressBar" className={styles.progressBar}>
<div className={styles.hasPlay} style={{ width: `${ progressInfo }%` }} />
<div id="audio-position" className={styles.position} />
</div>
</section>
点击进度条不同的位置,更新进度:
通过onMouseDown监听事件,实现点击更新进度条,具体代码如下:
// 播放定位 const changeDuration = (e: any) => {
// 获取距离左边的距离
const audioNode = document.getElementById('audio-area') as HTMLAudioElement;
let progressBar = document.getElementById('progressBar') as HTMLDivElement;
let progressBarRect = progressBar.getBoundingClientRect();
let progressBarPosition = progressBarRect.x; // 获取指标在的位置
let position = document.getElementById('audio-position') as HTMLDivElement;
let positionRect = position.getBoundingClientRect();
let curPosition = Math.ceil(positionRect.x + positionRect.width / 2 - progressBarPosition); // 获取当前点击的位置
let clickPosition = e.clientX - progressBarPosition;
if(curPosition) {
let newDuration = Math.ceil(currentDuration * clickPosition / curPosition);
setCurrentDuration(newDuration);
audioNode.currentTime = newDuration;
}
}
通过onTouchMove监听,手指滑动的进度条变更,代码如下:
const onTouchMove = (e: any) => {
// 获取距离左边的距离
const audioNode = document.getElementById('audio-area') as HTMLAudioElement;
let progressBar = document.getElementById('progressBar') as HTMLDivElement;
let progressBarRect = progressBar.getBoundingClientRect();
let progressBarPosition = progressBarRect.x; // 获取指标在的位置
let position = document.getElementById('audio-position') as HTMLDivElement;
let positionRect = position.getBoundingClientRect();
let curPosition = Math.ceil(positionRect.x + positionRect.width / 2 - progressBarPosition);
// 获取当前点击的位置 let clickPosition = e.touches[0].clientX; - progressBarPosition;
if(curPosition) {
let newDuration = Math.ceil(currentDuration * clickPosition / curPosition);
setCurrentDuration(newDuration);
audioNode.currentTime = newDuration;
}
};