使用的技术栈 ( vue3.0+typeScript+Element Plus)
推荐封装成组件 在现有的项目中使用 。 vue2.x 可以需要修改 不支持ts可以将相关的类型判断去掉即可 需要不同的样式 需要不同的样式 可以自己定义
h5 audio 相关事件以及属性
<audio
ref="audio"
class="audioStyle"
:src="url"
:preload="audioState.preload"
@play="onPlay"
@error="onError"
@waiting="onWaiting"
@pause="onPause"
@timeupdate="onTimeupdate"
@loadedmetadata="onLoadedmetadata"
>
</audio>
1. src 就是音频的地址
2. preload 属性规定是否在页面加载后载入音频
- auto 当页面加载后载入整个音频
- meta 当页面加载后只载入元数据
- none 当页面加载后不载入音频
3. onPlay 播放方法
4. onPause 暂停方法
5. onTimeupdate 当timeupdate事件大概每秒一次,
用来更新音频流的当前播放时间 此时也可以更新进度条
6. loadedmetadata 加载音频的时长
定义播放录音的样式以及相关方法使用
1. 通过 onLoadedmetadata 来加载音频的时长
const onLoadedmetadata = (res)=> {
state.audioState.waiting = false
state.audioState.maxTime = parseInt(res.target.duration)
}
2. 点击暂停或者播放按钮来调用 audio 的对应功能
const audio = ref(); // vue3.x的写法 这里不用管
const startPlayOrPause=()=> {
return state.audioState.playing ? pausePlay() : startPlay()
}
// 开始播放
const startPlay =()=> {
audio.value.play()
}
// 暂停
const pausePlay = ()=> {
audio.value.pause()
}
3. 拖拽播放进度条实时更新播放时间
// 播放跳转
const changeCurrentTime = (index)=> {
audio.value.currentTime = Math.floor((index) / 100 * state.audioState.maxTime))
}
4. 改变音量大小
// 音量改变通过 audio 的 volume属性
const changeVolume = (index = 0)=> {
audio.value.volume = Number(index) / 100
state.volume = index
}
5. 下载录音文件
const downloadFile=()=>{
let temurl=state.url.split('.').slice(-2)[0].split('/').slice(-1)[0]
var laststr=temurl.lastIndexOf('_');
var newStr=temurl.substring(0,laststr);
let itemName=newStr
download(state.url,itemName)
}
const download =(url, filename)=> {
url=url+`?${Math.random()}`
getBlob(url, function(blob:any) {
saveAs(blob, filename);
})
}
const getBlob=(url:String,cb:any)=> {
var xhr:any = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function() {
if (xhr.status === 200) {
cb(xhr.response);
}
};
xhr.send();
}
const saveAs=(blob, filename)=> {
var link:any = document.createElement('a');
var body:any = document.querySelector('body');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
// fix Firefox
link.style.display = 'none';
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
6. 快进 通过修改 audio 的playbackRate 属性
const changeSpeed =()=> {
let index = state.speeds.indexOf(state.audioState.speed) + 1
state.audioState.speed = state.speeds[index % state.speeds.length]
audio.value.playbackRate = state.audioState.speed
}
完整代码
<template>
<div class="audio_player">
<audio
ref="audio"
class="audioStyle"
:src="url"
:preload="audioState.preload"
@play="onPlay"
@error="onError"
@waiting="onWaiting"
@pause="onPause"
@timeupdate="onTimeupdate"
@loadedmetadata="onLoadedmetadata"
>
</audio>
<!-- 功能配置菜单 功能关闭 -->
<div class="playevent">
<!-- 播放暂停 -->
<el-image
class="hoverClass"
@click="startPlayOrPause"
style="width: 30px; height: 30px"
:src="playStatusurlArr[audioState.playing]"
fit="scale-down"
>
</el-image>
<div class="split_style"></div>
<!-- 快进可以配置 功能关闭 -->
<el-button
v-if="false"
type="text"
@click="changeSpeed">{{ '快进: x' + audioState.speed }}
</el-button>
<el-button
type="text"
style="color:#909399;">{{ realFormatSecond(audioState.currentTime)}}
</el-button>
<div class="content_solid">
<el-slider
class="slider"
v-model="sliderTime"
:format-tooltip="formatProcessToolTip"
@change="changeCurrentTime" >
</el-slider>
</div>
<el-button type="text" style="color:#909399;">
{{realFormatSecond(audioState.maxTime)}}
</el-button>
<div class="split_style"></div>
<!-- 静音功能可以配置 功能关闭 -->
<el-button
v-if="false" type="text"
@click="startMutedOrNot">{{audioState.muted ? '放音' : '静音'}}
</el-button>
<div class="voice_solid">
<el-image
@click="moreMiniVoice"
class="hoverClass"
style="width: 30px; height: 30px; margin-right:8px;"
:src="playStatusurlArr['sound']"
fit="scale-down"
>
</el-image>
<el-slider
class="sliderVoice"
v-model="volume"
:format-tooltip="formatVolumeToolTip"
@change="changeVolume" >
</el-slider>
<el-image
@click="moreMaxVoice"
class="hoverClass"
style="width: 30px; height: 30px"
:src="playStatusurlArr['moresound']"
fit="scale-down"
>
</el-image>
</div>
<div class="split_style" style="margin-right: 12px;"></div>
<!-- 下载 -->
<el-image
class="hoverClass"
@click="downloadFile"
style="width: 30px; height: 30px"
:src="playStatusurlArr['download']"
fit="scale-down"
>
</el-image>
</div>
</div>
</template>
<script lang="ts">
import { onMounted, reactive, toRefs, ref } from 'vue';
export default {
name: 'audioPlayer',
props: {
theUrl: {
type: String,
required: true,
},
theSpeeds: {
type: Array,
default () {
return [1, 1.5, 2]
}
},
theControlList: {
type: String,
default: ''
}
},
setup(props:any, ctx:any) {
const state=reactive({
url: props.theUrl,
audioState: {
currentTime: 0,
maxTime: 0,
playing: false,
muted: false,
speed: 1,
waiting: true,
preload: 'auto'
},
sliderTime: 0,
volume: 50,
speeds: props.theSpeeds,
/**
* 控制功能数据
*/
controlList: {
// 可以配置传入参数是显示具体功能
},
// 此处需要配置自己的图片效果
playStatusurlArr:{
false:'',
true:'',
'download':'',
'sound':'',
'moresound':'',
}
})
setControlList()
// 当音频开始播放
const onPlay = (res:any)=> {
state.audioState.playing = true
if(!state.controlList.onlyOnePlaying){
return
}
let target = res.target
let audios = document.getElementsByTagName('audio');
[...audios].forEach((item) => {
if(item !== target){
item.pause()
}
})
}
// 当发生错误, 就出现loading状态
const onError = ()=> {
state.audioState.waiting = true
}
// 当音频开始等待
const onWaiting = (res:any) => {
console.log(res)
}
// 当音频暂停
const onPause = ()=> {
state.audioState.playing = false
}
// 当timeupdate事件大概每秒一次,用来更新音频流的当前播放时间
const onTimeupdate =(res:any)=> {
state.audioState.currentTime = res.target.currentTime
state.sliderTime = Math.floor(Number(state.audioState.currentTime) / Number(state.audioState.maxTime) * 100)
}
const onLoadedmetadata = (res:any)=> {
state.audioState.waiting = false
state.audioState.maxTime = parseInt(res.target.duration)
}
/**
* 以下是按钮触发事件
*/
const audio = ref();
const startPlayOrPause=()=> {
return state.audioState.playing ? pausePlay() : startPlay()
}
// 开始播放
const startPlay =()=> {
audio.value.play()
}
// 暂停
const pausePlay = ()=> {
audio.value.pause()
}
const changeSpeed =()=> {
let index = state.speeds.indexOf(state.audioState.speed) + 1
state.audioState.speed = state.speeds[index % state.speeds.length]
audio.value.playbackRate = state.audioState.speed
}
const realFormatSecond=(second:any)=> {
var secondType = typeof second
if (secondType === 'number' || secondType === 'string') {
second = parseInt(second)
var hours = Math.floor(second / 3600)
second = second - hours * 3600
var mimute = Math.floor(second / 60)
second = second - mimute * 60
return hours + ':' + ('0' + mimute).slice(-2) + ':' + ('0' + second).slice(-2)
} else {
return '0:00:00'
}
}
// 进度条toolTip
const formatProcessToolTip =(index = 0)=> {
index =Math.floor(Number(state.audioState.maxTime) / 100 * index)
return '进度条 : ' + realFormatSecond(index)
}
// 播放跳转
const changeCurrentTime = (index:any)=> {
audio.value.currentTime = Math.floor(Number(index) / 100 * Number( state.audioState.maxTime))
}
const startMutedOrNot =()=> {
audio.value.muted = !audio.value.muted
audio.value.muted = audio.value.muted
}
// 音量条toolTip
const formatVolumeToolTip=(index:any)=> {
return '音量条 : ' + index
}
// 音量改变
const changeVolume = (index:any = 0)=> {
audio.value.volume = Number(index) / 100
state.volume = index
}
const moreMiniVoice =()=>{
audio.value.volume = 0
state.volume = 0
}
const moreMaxVoice =()=>{
audio.value.volume = 1
state.volume = 100
}
// 下载文件
const downloadFile=()=>{
let temurl=state.url.split('.').slice(-2)[0].split('/').slice(-1)[0]
var laststr=temurl.lastIndexOf('_');
var newStr=temurl.substring(0,laststr);
let itemName=newStr
download(state.url,itemName)
}
const download =(url:any, filename:String)=> {
url=url+`?${Math.random()}`
getBlob(url, function(blob:any) {
saveAs(blob, filename);
})
}
const getBlob=(url:String,cb:any)=> {
var xhr:any = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function() {
if (xhr.status === 200) {
cb(xhr.response);
}
};
xhr.send();
}
const saveAs=(blob:any, filename:any)=> {
var link:any = document.createElement('a');
var body:any = document.querySelector('body');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
// fix Firefox
link.style.display = 'none';
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
onMounted(() => {
// 点击的时候自动播放
// startPlayOrPause()
})
return {
...toRefs(state),
onPlay,
onError,
onWaiting,
onPause,
onTimeupdate,
onLoadedmetadata,
startPlayOrPause,
audio,
changeSpeed,
realFormatSecond,
formatProcessToolTip,
changeCurrentTime,
startMutedOrNot,
formatVolumeToolTip,
changeVolume,
setControlList,
downloadFile,
download,
getBlob,
saveAs,
moreMiniVoice,
moreMaxVoice,
}
}
}
</script>
<style scope>
.audio_player {
display: inline-block;
padding:4px 16px 4px 16px;
border-radius: 4px;
}
.audioStyle {
display: none;
}
.slider {
display: inline-block;
width: 190px;
margin: 0 15px ;
margin-top: 4px;
}
.sliderVoice {
display: inline-block;
width: 100px;
margin-right:16px;
}
.download {
color: #409EFF;
margin-left: 15px;
}
.playevent {
display: flex;
align-items: center;
justify-content: center;
}
.split_style {
border-left:1px solid #DCDFE6;
height:38px;
margin:0 16px;
width: 1px;
}
.hoverClass:hover {
cursor: pointer;
}
</style>
<style>
.audio_player .el-button {
display: inline-block;
line-height: 1;
min-height: 24px;
padding:0;
}
.audio_player .el-dialog__header {
padding: 0;
border-bottom:none;
}
.content_solid .el-slider__button {
display: inline-block;
width: 10px;
height: 10px;
vertical-align: middle;
border: 1px solid #025BAC;
border-radius: 50%;
box-sizing: border-box;
box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.3);
transition: .1s;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background:#025BAC;
}
.voice_solid .el-slider__button {
width: 16px;
height: 16px;
background: #FFFFFF;
border: 1px solid #fff;
box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.3);
}
.voice_solid {
display:flex;
align-items:center;
}
</style>
以上是全部组件里面的代码 建议全部copy 进行测试以及修改
定义组件
使用组件
组件传参