当前播放歌词高亮:
根据当前播放时间,和歌词时间对比,获得当前歌词行数索引
正常居中滚动、点击/滑动进度条对应滚动:
(1)因为这几种方式都是改变当前播放时间,所以只需要根据当前播放时间操作即可
(2)根据当前播放时间,得到当前歌词行数,因为设置歌词和空白高度都是一定的,所以可以根据行数确定居中位置
(3)当行数小于指定行或大于指定行时,滚动顶部/底部,在其中居中
居中方式:将滚动条移动=当前行到顶部的距离(-90是因为父元素到滑动区域的距离有90)-行高度*在多少(n)行就可居中
效果图:
代码示例:
<template>
<div class='s'>
<div class='scale' @click="scale">
<span><a-icon type="fullscreen-exit" /></span>
</div>
<div class='b-i'>
<img height="100px" :src="imgUrl" alt="">
</div>
<div class='c-c' :class={done:!isplay,on:isplay}>
<img :src="imgUrl" alt="">
</div>
<div class='c-l'>
<div class='c-i'>
<p>海阔天空</p>
<div>歌手: beyond</div>
</div>
<div class='c-lyr' ref='scroll' >
<ul ref='ul'>
<!-- :class='{hight:high(item.time,lyrs[index+1].time,time)}' -->
<!-- :class='{hight:time>item.time&&time<lyrs[index+1].time}' -->
<!-- high(index,lyrs,time) -->
<li v-for="(item,index) in lyrs" :key='index' :class='{hight:index==highlightLine}'>
{{item.lyr}}
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name:'song',
props:['isplay','time'],
data(){
return{
imgUrl:'',
lyric:'',
lyrs:[],
lis:[],
tops:[],
highlightLine:0
}
},
methods:{
scale()
{
this.$emit('mini')
},
//正则处理歌词
regRex(str)
{
// var reg= /(ru)n(oob)/i
//获取歌词贡献者
let regUser=/\[(.*)\]\n/igm
let user=regUser.exec(str);
str=str.substring(user[0].length,str.length);
//匹配歌词
let reg=/\[(.*)\](.*)\n/igm
let res;
while(res=reg.exec(str))
{
this.lyrs.push({time:this._timeForm(res[1]),lyr:res[2]});
}
console.log(this.lyrs);
},
//将字符串xx:xx的时间格式处理成秒
_timeForm(time)
{
let index=time.indexOf(':');
let min=time.substring(0,index);
let seconds=time.substring(index+1,time.length);
return min*60+seconds*1
},
//获取每一个歌词li的高度
_initTops(arr)
{
let tops=[];
Array.from(arr).forEach((item,index)=>{
tops.push(item.offsetTop);
})
this.tops=tops;
console.log(this.tops);
},
//监听任意方式得到的当前播放时间,来进行对象滚动,当index在歌词前五行和后无行时,进行居中
//歌词和空白都是固定高度,所以可以通过歌词的行数来确定居中
//居中方式:当前行到顶部的距离(-90是因为父元素到滑动区域的距离有90)-在多少行就可以居中
_scrollTo(index)
{
console.log(index);
if(index<5)
{
this.$refs.scroll.scrollTop=0;
}else if(index>=(this.lyrs.length-5))
{
this.$refs.scroll.scrollTop=this.$refs.scroll.scrollHeight;
}else{
this.$refs.scroll.scrollTop=this.tops[index]-90-(this.tops[index+1]-this.tops[index])*4
}
}
},
computed()
{
song()
{
// if
}
},
async mounted () {
let imgRes=await axios.get('https://api.imjad.cn/cloudmusic/?type=detail&id=346089');
let lyricRes=await axios.get('https://api.imjad.cn/cloudmusic/?type=lyric&id=346089');
this.imgUrl=imgRes.data.songs[0].al.picUrl;
this.lyric=lyricRes.data.lrc.lyric;
this.regRex(this.lyric);
},
watch:{
lyrs()
{
this.$nextTick(()=>{
this._initTops(this.$refs.ul.children);
console.log(this.$refs.ul.children);
})
},
//监听当前播放时间,和转换后的歌词时间进行对比,获得当前时间对应的歌词行数
//第一行时间之前,直接制顶,否则会因为findIndex找不到从而获取到最后一行的索引
time(time)
{
if(time<this.lyrs[0].time)
{
this.$refs.scroll.scrollTop=0;
return ;
}
let index=this.lyrs.findIndex((item,index)=>{
if(index<this.lyrs.length-1)
{
return item.time<=time&&this.lyrs[index+1].time>=time
}else{
return true;
}
})
console.log('第二个');
this._scrollTo(index);
this.highlightLine=index;
}
},
}
</script>
<style scoped lang='less'>
.s{
position: relative;
height: 500px;
width: 100%;
>.scale{
position: absolute;
right: 40px;
top:40px;
width: 30px;
height: 20px;
border:solid 1px #ccc;
border-radius: 4px;
text-align: center;
line-height: 20px;
cursor: pointer;
}
.b-i{
position: absolute;
left: 25%;
>img{
width: 400px;
height: 400px;
filter:blur(20px) opacity(0.5);
}
}
.c-c{
height: 300px;
width: 300px;
border-radius: 300px;
background: #2B2C2F;
text-align: center;
line-height: 300px;
position: absolute;
left: 0;
margin: 80px 80px;
animation: rot 20s linear infinite;
>img{
height: 200px;
width: 200px;
border-radius: 200px;
}
}
.on{
animation-play-state:running;
}
.done{
animation-play-state:paused;
}
@keyframes rot {
from {
transform: rotate(0deg);
}
to{
transform: rotate(360deg);
}
}
.c-l{
height: 450px;
width: 400px;
position: absolute;
overflow: hidden;
right: 70px;
top:40px;
box-sizing: border-box;
display: flex;
flex-direction: column;
.c-i{
flex: 1;
>p{
font-weight: 500;
font-size:20px;
margin-bottom:10px;
}
margin-bottom:30px;
}
.c-lyr{
flex:8;
width: 100%;
overflow-y:scroll ;
>ul{
height: 100%;
list-style: none;
li{
height: 35px;
// padding: 7px 0;
line-height: 35px;
color: #555254;
}
.hight{
color:white;
}
}
}
.c-lyr::-webkit-scrollbar {
width: 8px;
background-color:#FAFAFA;
}
.c-lyr::-webkit-scrollbar-thumb {
border-radius: 10px;
background-color:#E1E1E2;
}
}
}
</style>