写一个音乐播放器3--歌曲播放进度条与歌词滚动同步
前言
实现的是拖动或点击进度条,歌词随之滚动到指定位置,做到与歌曲播放同步。
进度条可参考写一个音乐播放器1--歌曲播放进度条歌词滚动可参考写一个音乐播放器2--歌词高亮滚动
实现
采用原生audio来实现歌曲播放
html布局部分
盒子布局依旧与前面一致
-
采用原生
audio
来实现音乐播放功能; -
进度条主体是由一个类名为
progress-bar
的父盒子包裹一个类名为blue-bar
的子盒子,其中父盒子为底部灰色进度条,子盒子为已播放的蓝色进度条; -
类名为
circle
的span盒子为进度条白色拖拽点cTime
为已播放时间,dTime
为总时间,采用固定定位。 -
采用无序列表
ul
加v-for
实现歌词布局,一行一句歌词,默认中间行为当前播放歌词,高亮显示它。
<audio ref="player" autoplay ></audio>
//歌词部分
<div class="detail">
<div class="song-title">
<p ref="song">歌名</p>
<p ref="singer">歌手</p>
</div>
<div class="wrapper">
<ul ref="ul" class="content">
<li v-for="(item,index) of ms" :key=item.index>{{item.c}}</li>
</ul>
</div>
</div>
//进度条
<div class="bar">
<div class="progressbar" @click="playMusic" ref="runfatbar">
<div class="greenbar" ref="runbar" >
<span class="yuan"></span>
</div>
</div>
</div>
//时间
<div class="time-text">{{cTime}}</div>
<div class="right-time time-text">{{dTime}}</div>
SCSS样式部分
歌词部分
//歌词部分
.detail {
position: absolute;
top: 1rem;
bottom: 2.6rem;
left: 0;
right: 0;
text-align: center;
color: #26a2ff;
.song-title {
width: 100%;
height: 2rem;
p {
width: 100%;
line-height: .8rem;
font-size: 18px;
color: #FFD700;
margin-top: .1rem;
text-align: center;
}
}
.wrapper {
overflow: hidden;
position: absolute;
top: 2rem;
right: 0;
left: 0;
height: 545px;
ul {
line-height: 32px;
width: 100%;
padding-bottom: 1rem;
li {
font-size: 16px;
transition-duration: 1200ms;
}
.lineHigh {
color: #FFD700;
}
}
}
}
进度条部分
//进度条部分
<style scoped lang="scss">
.bar {
width: 80%;
height: .3rem;
position: fixed;
bottom: 2.5rem;
margin-left: 10%;
.progress-bar {
width: 100%;
height: .1rem;
margin-top: .1rem;
border-radius: .2rem;
background-color: #999999;
.blue-bar {
height: .1rem;
position: absolute;
top: .1rem;
left: 0;
border-radius: .2rem;
background-color: #1296db;
.circle {
width: .2rem;
height: .2rem;
position: absolute;
top: -.05rem;
right: -0.01rem;
border-radius: 10px;
background-color: #fff;
}
}
}
}
</style>
时间部分
//时间部分
.time {
position: fixed;
bottom: 2.5rem;
font-size: 13px;
line-height: .5rem;
}
.all-time {
right: 0;
}
逻辑部分
这里对每一行进行了解释,便于通篇理解。
data () {
return {
lineNo: 0,
Cpos: 7,
offset: -32,
ms: [],
}
},
mounted () {
const music = this.$refs.player // 音频所在对象
const musicBar = this.$refs.runbar // 颜色进度条所在对象
const musicWidth = this.$refs.runfatbar.offsetWidth // 底部进度条总宽 // 监听颜色进度条是否触摸拖动结束
musicBar.addEventListener('touchend', () => {
const touwidth = (musicBar.offsetWidth / musicWidth) // 计算进度条所在比例
music.currentTime = music.duration * touwidth // 通过所在比例赋值给音频应在的播放时间
const ulist = this.$refs.ul
const list = ulist.getElementsByTagName("li");//获取全部歌词
for(var i = 0; i <= this.ms.length; i++) {
if(parseFloat(this.ms[i].t) <= music.currentTime) {
// 删除之前的高亮样式与设置当前点击部分高亮样式
if(this.lineNo < i) {
list[this.lineNo].removeAttribute("class");//去掉上面的高亮样式
this.lineNo = i
list[this.lineNo].className = "lineHigh";//高亮显示当前行
list[this.lineNo-1].removeAttribute("class");//去掉下面的高亮样式
}else {
this.lineNo = i
list[this.lineNo].className = "lineHigh";//高亮显示当前行
}
// 进行播放
music.play()
this.$refs.icon.innerHTML = ("");
}
}
})
},
methods: {
// 点击进度条事件
playMusic (e) {
const music = this.$refs.player // 音频所在对象
const barWidth = e.pageX / this.$refs.runfatbar.offsetWidth // 计算点击位置相对父元素总宽的比例
this.$refs.runbar.style.width = `${barWidth * 100}%` // 进度条应所在的比例总宽
music.currentTime = music.duration * barWidth // 计算点击时应播放所在的时间
const ulist = this.$refs.ul
const list = ulist.getElementsByTagName("li");
for(var i = 0; i <= this.ms.length; i++) {
if(parseFloat(this.ms[i].t) <= music.currentTime) {
// 删除之前的高亮样式与设置当前点击部分高亮样式
if(this.lineNo < i) {
list[this.lineNo].removeAttribute("class");//去掉上面的高亮样式
this.lineNo = i
list[this.lineNo].className = "lineHigh";//高亮显示当前行
}else {
list[this.lineNo-1].removeAttribute("class");//去掉下面的高亮样式
this.lineNo = i
list[this.lineNo].className = "lineHigh";//高亮显示当前行
}
// 进行播放
music.play()
this.$refs.icon.innerHTML = ("");
}
}
},
}
最后
不知道是否复杂化,欢迎提出建议鸭。

转载自:https://juejin.cn/post/7132078096663969822