记一次需求ui小姐姐要求音频控件的样式要求如下图中的样式
其中上面控件就是ui需要完成的样子,而下面的则是音频原样式,
原本想强制更改audio标签的样式,后来发现更改起来并非想象中的那么容易,因为上下俩之间的差异还是比较大的,所以便想到了自己i手写一个,后续发现vantui也有进度条的组件,所以就基于vantui的slider组件进行开发,可是在开发的过程发现几个问题,
问题一:音频总时长和slide组件的总进度匹配,slider组件有max属性可双向数据绑定,而音频总时长则可以通过udio标签loadedmetadata方法之后再经过ref获取标签的方式来获取音频来获取时长对应代码
<van-slider
inactive-color="rgba(4,115,255,0.1)"
v-model="value"
bar-height="5px"
:max="audioDuration"
@change="change">
<template #button>
<div class="custom-button"></div>
</template>
</van-slider>
<audio
v-show="false"
controls
:src="videoSrc"
ref="toplay"
@loadedmetadata="getDuration"
@ended="handleAudioEnded"></audio>
getDuration() {
const ad = this.$refs.toplay;
this.audioDuration = ad.duration;
this.endTime = this.formatTime(ad.duration);
},
问题二:左右时间逻辑:左侧动态时间可以通过audio标签的 ad.ontimeupdate更新方式来获取音频的播放进度,而右侧则通过总时长formatTime()转化则可以得到
if (!this.$isEmpty(this.audioDuration)) {
ad.ontimeupdate = () => {
this.value = ad.currentTime;
if (this.value > this.audioDuration) this.value = 0;
this.startTime = this.formatTime(ad.currentTime);
};
}
formatTime(seconds) {
const minutes = Math.floor(seconds / 60).toFixed(0);
const remainingSeconds = (seconds % 60).toFixed(0);
const minutesString = String(minutes).padStart(2, '0');
const secondsString = String(remainingSeconds).padStart(2, '0');
return `${minutesString}:${secondsString}`;
},
问题三:播放图标变化:阿里或者ui提供图标,再通过自定义方法play(),以及audio标签中方法 ad.play()和 ad.pause()控制音频的播放和停止,且同时控制图标的切换
play() {
const ad = this.$refs?.toplay;
if (ad.paused) {
ad.play();
this.src = play;
this.codeName = '停止';
} else {
ad.pause();
this.src = stop;
this.codeName = '播放';
}
},
问题四:拖拽进度条控制音频的播放位置,组件含有change方法,可以监听用户拖拽的位置,然后在通过audio标签的 ad.currentTime属性去控制音频的播放进度,
change(value) {
const ad = this.$refs.toplay;
ad.currentTime = value;
},
总结所有代码逻辑如下
<template>
<div>
<div class="flex-center-center audio">
<img :src="src" alt="" srcset="" @click="play" class="m_l24 m_r16" />
<span class="time m_r24">{{ startTime }}</span>
<van-slider
inactive-color="rgba(4,115,255,0.1)"
v-model="value"
bar-height="5px"
:max="audioDuration"
@change="change">
<template #button>
<div class="custom-button"></div>
</template>
</van-slider>
<span class="time m_l24 m_r24">{{ endTime }}</span>
</div>
<audio
v-show="false"
controls
:src="videoSrc"
ref="toplay"
@loadedmetadata="getDuration"
@ended="handleAudioEnded"></audio>
</div>
</template>
<script>
import play from '@/assets/newApp/play.png';
import stop from '@/assets/newApp/stop.png';
export default {
props: {
show: { type: Boolean, default: false },
videoSrc: { type: String, default: '' },
},
data() {
return {
src: stop,
codeName: '播放',
audioDuration: null,
startTime: '00:00',
endTime: '00:00',
value: 0,
};
},
computed: {
config() {
return this.$store.state.config;
},
},
methods: {
getDuration() {
const ad = this.$refs.toplay;
this.audioDuration = ad.duration;
this.endTime = this.formatTime(ad.duration);
if (!this.$isEmpty(this.audioDuration)) {
ad.ontimeupdate = () => {
this.value = ad.currentTime;
if (this.value > this.audioDuration) this.value = 0;
this.startTime = this.formatTime(ad.currentTime);
};
}
},
formatTime(seconds) {
const minutes = Math.floor(seconds / 60).toFixed(0);
const remainingSeconds = (seconds % 60).toFixed(0);
const minutesString = String(minutes).padStart(2, '0');
const secondsString = String(remainingSeconds).padStart(2, '0');
return `${minutesString}:${secondsString}`;
},
change(value) {
console.log(value, 'value');
const ad = this.$refs.toplay;
ad.currentTime = value;
},
handleAudioEnded(val) {
const ad = this.$refs?.toplay;
if (val && ad) {
ad.currentTime = 0; // 将音乐的当前时间设置为0,以重新播放
this.codeName = '播放';
this.src = stop;
}
},
play() {
const ad = this.$refs?.toplay;
if (ad.paused) {
ad.play();
this.src = play;
this.codeName = '停止';
} else {
ad.pause();
this.src = stop;
this.codeName = '播放';
}
},
},
watch: {
show(val) {
const ad = this.$refs?.toplay;
if (!val && ad) {
ad.currentTime = 0; // 将音乐的当前时间设置为0,以重新播放
ad.pause();
this.codeName = '播放';
this.src = stop;
}
},
},
};
</script>
<style lang="scss" scoped>
audio {
width: 702px;
height: 74px;
background: #ffffff;
border-radius: 37px;
border: 1px solid #cde3ff;
position: relative;
}
.audio {
width: 702px;
height: 74px;
background: #ffffff;
border-radius: 37px;
border: 1px solid #cde3ff;
img {
width: 46px;
height: 46px;
}
.custom-button {
width: 18px;
height: 18px;
background: #0473ff;
border-radius: 9px;
}
.time {
min-width: 75px;
height: 36px;
font-size: 28px;
color: #1c1c1e;
line-height: 36px;
}
.m_r24 {
margin-right: 24px;
}
.m_l24 {
margin-left: 24px;
}
.m_r16 {
margin-right: 16px;
}
}
</style>