实现歌词和歌曲在时间上的绑定的原理是,通过事件获取歌曲的当前播放事件,再和歌词的时间进行对比
使用插件:lyric-parser
,js-base64
如果后台请求回来的歌词是base64,就使用js-base64
进行转换
命令行下载
npm install lyric-parser
npm install js-base64
在组件内引入
import Lyric from 'lyric-parser'
import {Base64} from 'js-base64'
-
将请求回来的歌词转换成以下形式
请求回来的值可能是这样的,所以需要用base64转换
W3RpOua8lOWRmF0KW2FyOuiWm+S5i+iwpl0KW2FsOue7heWjq10KW2J5Ol0KW29mZnNldD
owXQpbMDA6MDAuNTZd5ryU5ZGYIC0g6Jab5LmL6LCmClswMDowMi40Ml3or43vvJrolpvk
uYvosKYKWzAwOjAzLjk5Xeabsu+8muiWm+S5i+iwpgpbMDA6MDUuMzhd57yW5puy77ya6Y
OR5LyfL+W8oOWuneWuhwpbMDA6MDcuNDVd5Yi25L2c5Lq677ya6LW16Iux5L+KClswMDow
OS4wOV3lkIjlo7DvvJrotbXoi7Hkv4oKWzAwOjEwLjQ4XeW9lemfs+W4iO+8mueOi+aZk+
a1twpbMDA6MTEuNzRdClswMDoxMi4zNF3mt7fpn7PluIjvvJrpso3plJAKWzAwOjEzLjg4X
eavjeW4puWkhOeQhuW3peeoi+W4iO+8mumyjemUkApbMDA6MTYuMTBdClswMDoyMS4yNV3n
roDljZXngrkKWzAwOjIyLjIxXQpbMDA6MjUuMTZd6K+06K+d55qE5pa55byP566A5Y2V54K
5ClswMDoyOC4yN10KWzAwOjMwLjIyXemAkui/m+eahOaDhee7quivt+ecgeeVpQpbMDA6Mz
MuNTdd5L2g5Y+I5LiN5piv5Liq5ryU5ZGYClswMDozNi4yN13liKvorr7orqHpgqPkupvmg
4XoioIKWzAwOjM5LjA4XQpbMDA6NDIuMjBd5rKh5oSP6KeBClswMDo0My43NF0KWzAwOjQ2
LjE3XeaIkeWPquaDs+eci+eci+S9oOaAjuS5iOWchgpbMDA6NDkuNDddClswMDo1MS43NF3
kvaDpmr7ov4fnmoTlpKrooajpnaIKWzAwOjU0LjYwXeWDj+ayoeWkqei1i+eahOa8lOWRmA
pbMDA6NTcuMjFd6KeC5LyX5LiA55y86IO955yL6KeBClswMDo1OS42OF0KWzAxOjAyLjQ2X
eivpemFjeWQiOS9oOa8lOWHuueahOaIkea8lOinhuiAjOS4jeingQpbMDE6MDcuMDJdClsw
MTowNy41Nl3lnKjpgLzkuIDkuKrmnIDniLHkvaDnmoTkurrljbPlhbTooajmvJQKWzAxOjE
yLjE5XQpbMDE6MTIuODZd5LuA5LmI5pe25YCZ5oiR5Lus5byA5aeL5pS26LW35LqG5bqV57
---------------------
作者:ECMAScripter
来源:CSDN
原文:https://blog.csdn.net/fabulous1111/article/details/79144799
版权声明:本文为博主原创文章,转载请附上博文链接!
转换后将对象使用父子通讯传到子组件去,并使用v-for循环输出
- 在父组件的生命钩子中创建Lyric对象,将获取到的歌曲字符串转换成对象
父&子组件皆可安装
this.ric = new Lyric(this.theLyric);
this.theLyric = this.ric.lines;
- 通过
audio
的timeupdate
事件触发函数,获取当前的时间currentTime
父组件:
<audio :src="songSrc" class="theSong" @timeupdate="update" ></audio>
父组件:
methods:{
update(){
this.theSong = document.querySelector('.theSong');
// console.log(~~this.theSong.currentTime)
// 存储当前的歌曲播放时间
// currentTime是audio dom元素的属性,获取当前时间
this.currentTime=~~this.theSong.currentTime;
}
}
- 比较歌曲和歌词的时间
由于我是用组件的形式进行传值,所以需要用
watch
进行数据的监听
父组件:
<audio :src="songSrc" class="theSong" @timeupdate="update" ></audio>
子组件:
props:['handleLyric','title','curTime'],
data(){
return {
curLyric:'',
lyrics:'',
lyricIndex:0
}
},
watch:{
curTime:function(newValue){
// 使用箭头函数,固定this的指向
// 遍历对象里面的时间(一般是毫秒),匹配歌曲的当前时间
this.handleLyric.forEach((el,index)=>{
if(~~(el.time/1000)==newValue){
console.log(index);
// 匹配数组的index(相当于距离),进行位移
this.lyricIndex=(-24)*index
}
})
}
}
- 进行歌词的位移
子组件:
<div class="song-iner" :style="{transform: 'translateY('+lyricIndex+'px)'}" > <!--transform: translateY(-24px);-->
至此就可以实现歌曲和歌词同步的效果
完整代码:
song.vue(父组件)
<template>
<div class="song">
<div class="bg" :style="{background:'url('+pic+') no-repeat',backgroundSize: 'cover',backgroundPosition:'50% 0%'}" >
<div class="bg-warp">
<mlogo class="logo"></mlogo>
<img src="https://s3.music.126.net/m/s/img/needle.png?702cf6d95f29e2e594f53a3caab50e12" class="musicNeedle" />
<div class="m-song-warp">
<div class="m-song-disc" @click="play();roll();music()" :style="{transform: 'rotate('+rollang+'deg)'}" >
<div class="m-song-turn" >
<!--点击后旋转的部分-->
<div class="m-song-rollwrap">
<div class="m-song-img">
<img :src="pic" class="u-img" />
<audio :src="songSrc" class="theSong" @timeupdate="update" ></audio>
</div>
</div>
</div>
</div>
<div class="m-song-plybtn" v-if="!isPlay" @click="play();roll();music()" ></div>
</div>
<song-info :handleLyric='theLyric' :title='songTitle' :curTime="currentTime" ></song-info>
</div>
</div>
</div>
</template>
<script>
import Lyric from 'lyric-parser'
import axios from 'axios'
import api from '@/api'
import mlogo from '@/svg/mlogo.svg'
import songInfo from '@/components/songInfo'
export default {
data(){
return {
list:'',
pic:'',
obj:{
backgroundColor: 'red',
backgroundImage: "url('+this.pic+')"
},
isPlay:true,
timer:'',
rollang:1,
songSrc:'',
theSong:'',
songTitle:'',
theLyric:'',
ric:'',
currentTime:''
}
},
methods:{
update(){
this.theSong = document.querySelector('.theSong');
// console.log(~~this.theSong.currentTime)
// 存储当前的歌曲播放时间
// currentTime是audio dom元素的属性,获取当前时间
this.currentTime=~~this.theSong.currentTime;
},
play(){
// this.rollang=this.rollang+1;
// console.log(this.rollang);
if(this.isPlay){
this.isPlay=false;
}else{
this.isPlay=true;
}
// console.log(this.isPlay)
// this.roll(this.isPlay);
},
roll(){
var rollwarp = document.querySelector(".m-song-rollwrap");
// clearInterval(this.timer);
if(this.isPlay==true){
this.timer = this.roler();
// console.log(this.rollang)
}else{
clearInterval(this.timer);
}
},
roler(){
return setInterval(()=>{
this.rollang=this.rollang+2;
// console.log(this.rollang);
},50)
},
music(){
this.theSong=document.querySelector('.theSong');
if(this.isPlay==true){
this.theSong.play();
}else{
this.theSong.pause();
}
}
},
name:'song',
mounted:async function(){
// console.log("mounted")
this.play();
this.$store.commit('hiddentHead',{
hiddentHead:true
})
let res = await axios.get(api.detail+"?ids="+this.$route.query.id);
let re = await axios.get(api.Lyric+this.$route.query.id);
this.list=res.data.songs[0];
this.pic=this.list.al.picUrl;
this.songSrc=api.songUrl+this.$route.query.id+'.mp3';
this.songTitle=this.list.name+'('+this.list.alia[0]+')';
// console.log(this.songTitle)
// 请求歌词
// console.log(re);
this.theLyric=re.data.lrc.lyric;
// console.log('hello')
this.ric = new Lyric(this.theLyric);
this.theLyric = this.ric.lines;
// console.log(this.theLyric);
},
activated:async function(){
// console.log("activated")
this.$store.commit('hiddentHead',{
hiddentHead:true
});
// async function a(){
// let res = await axios.get(api.detail+"?ids="+this.$route.query.id);
// console.log(res)
// }
let res = await axios.get(api.detail+"?ids="+this.$route.query.id);
let re = await axios.get(api.Lyric+this.$route.query.id);
this.list=res.data.songs[0];
this.pic=this.list.al.picUrl;
this.songSrc=api.songUrl+this.$route.query.id+'.mp3';
this.songTitle=this.list.name+'('+this.list.alia[0]+')';
// console.log(this.songTitle)
// 请求歌词
// console.log(re);
this.theLyric=re.data.lrc.lyric;
// console.log(this.theLyric);
// console.log('hi')
this.ric = new Lyric(this.theLyric);
this.theLyric = this.ric.lines;
// console.log(this.theLyric);
},
components:{
mlogo,
songInfo
}
}
</script>
<style lang="scss" scoped="scoped">
@import '../scss/common';
@import '../scss/reset';
.bg{
width: 100%;
height: r(1136px);
position: relative;
&::after{
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
filter: blur(10px);
background: inherit;
}
}
.bg-warp{
width:100%;
height:100%;
position:absolute;
top: 0;
left: 0;
background-color: rgba(0,0,0,0.6);
z-index: 50;
text-align: left;
}
.logo{
width: r(200px);
margin-left: r(30px);
}
.musicNeedle{
width: r(168px);
position: absolute;
top: 0;
left: r(285px);
z-index: 100;
}
.m-song-warp{
width: 100%;
margin-top: r(50px);
.m-song-disc{
position: relative;
width: r(496px);
height: r(496px);
margin: 0 auto;
transition: transform 0.05s;
}
}
.m-song-turn{
/*position: absolute;
z-index: 99;*/
width: 100%;
height: 100%;
&::before{
content: " ";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 2;
background: url(https://s3.music.126.net/m/s/img/disc.png?d3bdd1080a72129346aa0b4b4964b75f) no-repeat;
background-size: contain;
}
}
.m-song-rollwrap{
position: absolute;
width: r(300px);
height: r(300px);
left: 50%;
top: 50%;
z-index: 1;
margin: r(-150px) 0 0 r(-150px);
.m-song-img{
width: 100%;
height: 100%;
border-radius: 50%;
overflow: hidden;
background: url(https://s3.music.126.net/m/s/img/disc_default.png?7c9b3adc16f5485c2bfbe8a540534188) no-repeat;
background-size: contain;
}
}
.u-img{
width: 100%;
vertical-align: middle;
}
.m-song-plybtn{
position: absolute;
width: r(100px);
height: r(100px);
left: 50%;
top: 50%;
-webkit-transform: translate(-50%,-50%);
transform: translate(-50%,-250%);
z-index: 1;
background: url(../../public/img/play.png) 0 0 no-repeat;
background-size: contain;
}
</style>
songInfo.vue(子组件)
<template>
<div class="songInfo">
<h2 class="song-h2">
{{title}}
</h2>
<div class="song-lrc">
<div class="song-scoll"> <!--style="height: 43px;"-->
<div class="song-iner" :style="{transform: 'translateY('+lyricIndex+'px)'}" > <!--transform: translateY(-24px);-->
<p class="songIritem" v-for="(item,index) in handleLyric"> <!--time表示毫秒-->
{{item.txt}}
<!--{{curTime}}-->
</p>
</div>
</div>
</div>
</div>
</template>
<script>
import Lyric from 'lyric-parser'
export default {
name:'songInfo',
props:['handleLyric','title','curTime'],
data(){
return {
curLyric:'',
lyrics:'',
lyricIndex:0
}
},
watch:{
curTime:function(newValue){
// 使用箭头函数,固定this的指向
// 遍历对象里面的时间(一般是毫秒),匹配歌曲的当前时间
this.handleLyric.forEach((el,index)=>{
if(~~(el.time/1000)==newValue){
console.log(index);
// 匹配数组的index(相当于距离),进行位移
this.lyricIndex=(-24)*index
}
})
}
}
}
</script>
<style lang="scss" scoped="scoped">
@import '../scss/common';
@import '../scss/reset';
.songInfo{
padding: 0 r(70px);
margin-top: r(50px);
}
.song-h2{
text-align: center;
font-size: r(30px);
line-height: 1.1;
color: #fefefe;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.song-lrc{
position: relative;
margin-top: r(28px);
.song-scoll{
height: r(86px);
line-height: 1.5;
font-size: r(26px);
/*height: r(144px);*/
overflow: hidden;
text-align: center;
color: hsla(0,0%,100%,.6);
}
}
.song-iner{
/*transform: translateY(r(-48px));*/
-webkit-transition: -webkit-transform .3s ease-out;
transition: -webkit-transform .3s ease-out;
transition: transform .3s ease-out;
transition: transform .3s ease-out,-webkit-transform .3s ease-out;
}
.songIritem{
padding-bottom: r(10px);
}
</style>