/
lyrics.vue
145 lines (137 loc) · 3.98 KB
/
lyrics.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<template>
<div class="lyrics-wrapper"
ref="lyricsWrapper"
@touchstart="scrollAbled = false"
@touchend="scrollAbled = true">
<div class="lyrics">
<p v-for="(item, key) in lyricsArr"
v-if="item"
:class="key == lyricIndex && 'gradient'"
:style="key == lyricIndex && lyricObj">{{ item }}</p>
</div>
</div>
</template>
<script>
/* =========================================================================
* 歌词轮播组件
* 已完成功能:
* 1: 歌词随播放进度自动滚动
* 已完成UI:
* 1: 当前播放歌词字体颜色随歌曲进度变化
* 原理简介:
* 通过QQ音乐API获取歌词信息,然后将歌词解码。通过
* lyricsAnalysis函数将歌词信息分解成时间、单个歌词、单句歌词耗时三个数组。
* 歌词颜色部分采用 background-image: linear-gradient 来染色(详情可参考:
* https://css-tricks.com/snippets/css/gradient-text/)
* 由于CSS3 Transition并不支持此属性因此采用Tween.js (https://github.com/tweenjs/tween.js)
* 来执行属性变化的计算,并通过Window.requestAnimationFrame来优化,但是这样做需要在
* 短时间内执行大量的DOM操作,因此这个效果仍然会带来大量的内存消耗
* ========================================================================= */
import TWEEN from '@tweenjs/tween.js';
import AlloyTouch from 'alloytouch';
import Transform from 'css3transform';
import { mapState, mapMutations } from 'vuex';
const NameSpace = 'playing';
export default {
name: 'lyrics',
mounted() {
// initialize scroll effects
this._initScroll();
},
data() {
return {
lyrics: {
linearGradient: 0
},
alloyTouch: {},
scroll: {},
// lineHeight: 42,
scrollAbled: true // stop scroll to element when touch
};
},
computed: {
...mapState(NameSpace, ['songState']),
lyricIndex() {
return this.songState.currentLyricIndex;
},
lyricDuration() {
return this.songState.currentLyricDuration;
},
lyricsArr() {
return this.songState.currentLyricArr;
},
lyricObj() {
let linearGradient = this.lyrics.linearGradient;
return {
'background-image': `-webkit-linear-gradient(left,rgb(49, 194, 124) ${linearGradient}%,#ffffff ${linearGradient}%)`
};
}
},
watch: {
lyricIndex() {
// modify the current lyric linear-gradient value
let lyrics = this.lyrics;
lyrics.linearGradient = 0; // reset progress
new TWEEN.Tween(lyrics)
.to({
linearGradient: 100
}, this.lyricDuration*1000)
.start();
animate();
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
}
// according the index scroll current lyric element
this.scrollAbled && this._scrollToElement();
}
},
methods: {
_initScroll() {
let scrollTouch = this.$refs.lyricsWrapper,
scrollTarget = scrollTouch.children[0];
Transform(scrollTarget, true)
this.alloyTouch = new AlloyTouch({
touch: scrollTouch,
target: scrollTarget,
sensitivity: .8,
initialValue: scrollTouch.offsetHeight/2 >> 0,
bindSelf: true,
max: 0,
min: -scrollTarget.offsetHeight,
// step: this.lineHeight,
property: 'translateY'
});
},
_scrollToElement() {
let scrollTouch = this.$refs.lyricsWrapper,
scrollTarget = scrollTouch.children[0].children,
currentLyric = scrollTarget[this.lyricIndex],
offsetToCenter = scrollTouch.offsetHeight/2;
this.alloyTouch.to(-currentLyric.offsetTop + offsetToCenter, 600);
}
}
};
</script>
<style lang="sass">
.lyrics-wrapper {
position: relative;
height: 100%;
overflow: hidden;
.lyrics {
display: flex;
align-items: center;
flex-direction: column;
.gradient {
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
p {
line-height: 42px;
text-align: center;
font-size: 16px;
color: rgba(255,255,255,.8);
}
}
}
</style>