Audio实现音频播放的实战

664 阅读4分钟

包括音频文件的展示、播放、暂停、切换、自定义的进度条、手动点击和滑动进度条切换音频播放进度等效果的实现。

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:  当音频文件下载完毕后就会调用这个方法,这个时候可以获得音频的时长。

**onError:**当音频出错,文件无法正常播放的时候,可能会调用这个方法。

**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();

音频的切换

如果有一个音频列表,需要从当前音频切换到下一个,通常会怎么做呢。

1)暂停当前文件
const audioNode = document.getElementById('audio-area') ;
audioNode.pause();
  1. 修改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;    
} 
};