Fork me on GitHub
余鸢

Vue音乐播放器开发(五):歌手详情页开发以及Vuex的使用二

Vue音乐播放器开发(四):歌手详情页开发以及Vuex的使用一

交互效果

图片跟随滑动变小

当歌曲列表往上滑动时,图片跟随滑动变小以展示更多歌曲数据,当往下拉时,图片跟着放大。

当向上滚动列表时有个底也要跟着向上滚,那就需要一个层,这个层在文字的下面,随着滚动有向上推的过程。在scroll组件之前添加这个classbg-layerdiv层。

1
2
3
4
5
6
<div class="bg-layer" ref="layer"></div>
<scroll class="list" :data="songs" ref="list">
<div class="song-list-wrapper">
<song-list :songs="songs"></song-list>
</div>
</scroll>

bg-layer有个向上回动的效果,当滚动时需要操作bg-layer层的transform外向的偏移,让它随着滚动,所以要监听滚动的距离。给scroll组件添加个probeType,在created时添加probeTypelistenScroll属性,这样就可以实时监听scroll位置

1
2
3
4
5
6
7
8
9
10
<scroll :probe-type="probeType" :listen-scroll="listenScroll" class="list" :data="songs" ref="list">
<div class="song-list-wrapper">
<song-list :songs="songs"></song-list>
</div>
</scroll>
created () {
this.probeType = 3
this.listenScroll = true
},

监听个scroll事件,首先要维护一个纵向滚动的值,在data里定义scrollY变量默认为0。

v37

1
2
3
4
5
6
7
8
9
10
data () {
return {
scrollY: 0
}
},
methods: {
scroll (pos) {
this.scrollY = pos.y
}
},

给scrollY赋值就可以实时拿到scrollY的值。接着要做的是在滚动时拿到scrollY的值来设置bg-layer的偏移量。

设置bg-layer的滚动,watch scrollY。获取layer的transform

1
2
3
4
5
6
watch: {
scrollY (newY) {
this.$refs.layer.style['transform'] = `translate3d(0,${newY}px,0)`
this.$refs.layer.style['webkitTransform'] = `translate3d(0,${newY}px,0)`
}
},

发现个问题,滚动时图片还是会露出来,是因为bg-layer层的高度是100%。如图:

v38

这里就要做个限制。bg-layer随着scroll滚动时并不是无限滚动的过程,只需要滚动到一个位置就行了。所以要设置它的最大的滚动距离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mounted () {
this.imageHeight = this.$refs.bgImage.clientHeight
// 最小translateY值
this.minTranslateY = -this.imageHeight
this.$refs.list.$el.style.top = `${this.imageHeight}px`
},
watch: {
scrollY (newY) {
// 最大滚动距离
let translateY = Math.max(this.minTranslateY, newY)
this.$refs.layer.style['transform'] = `translate3d(0,${translateY}px,0)`
this.$refs.layer.style['webkitTransform'] = `translate3d(0,${translateY}px,0)`
}
},

记录背景图的高度和最小translateY值,计算出最大滚动距离。

为了不让bg-layer滚动到顶部,只需要修改minTranslateY,这里定义个常量RESERVED_HEIGHT高度为40的偏移量。

1
2
3
4
5
6
7
const RESERVED_HEIGHT = 40
mounted () {
this.imageHeight = this.$refs.bgImage.clientHeight
this.minTranslateY = -this.imageHeight + RESERVED_HEIGHT
this.$refs.list.$el.style.top = `${this.imageHeight}px`
},

效果:

V39

红线圈出的部分就是RESERVED_HEIGHT的高度。

由图可以看出,当滚动到顶部时文字会遮住图片,实际效果是希望滚动到定不是图片会遮住文字。

bg-layer层的position为relative布局的,设置z-index为10就会发现图片会遮住文字,但又会发现这时图片的高度是不对的,图片完全遮住文字就没有刚才的效果了,这里需要修改z-index和图片的高度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
watch: {
scrollY (newY) {
let translateY = Math.max(this.minTranslateY, newY)
let zIndex = 0
this.$refs.layer.style['transform'] = `translate3d(0,${translateY}px,0)`
this.$refs.layer.style['webkitTransform'] = `translate3d(0,${translateY}px,0)`
// 当滚动到顶部时
if (newY < this.minTranslateY) {
zIndex = 10
this.$refs.bgImage.style.paddingTop = 0
this.$refs.bgImage.style.height = `${RESERVED_HEIGHT}px`
} else {
this.$refs.bgImage.style.paddingTop = '70%'
this.$refs.bgImage.style.height = 0
}
this.$refs.bgImage.style.zIndex = zIndex
}
},

这样跟随滚动的效果就实现,同时达到了滚动到顶部overflowhidden的状态

图片跟随滚动放大效果

当鼠标下拉时,图片跟着有放大的效果,修改transform的scale,定义scale默认为1。

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
watch: {
scrollY (newY) {
let translateY = Math.max(this.minTranslateY, newY)
let zIndex = 0
let scale = 1
this.$refs.layer.style['transform'] = `translate3d(0,${translateY}px,0)`
this.$refs.layer.style['webkitTransform'] = `translate3d(0,${translateY}px,0)`
// 计算scale的比例
const percent = Math.abs(newY / this.imageHeight)
// 往下拉时计算scale的值
if (newY > 0) {
scale = 1 + percent
zIndex = 10
}
// 当滚动到顶部时
if (newY < this.minTranslateY) {
zIndex = 10
this.$refs.bgImage.style.paddingTop = 0
this.$refs.bgImage.style.height = `${RESERVED_HEIGHT}px`
} else {
this.$refs.bgImage.style.paddingTop = '70%'
this.$refs.bgImage.style.height = 0
}
this.$refs.bgImage.style.zIndex = zIndex
this.$refs.bgImage.style['transform'] = `scale(${scale})`
this.$refs.bgImage.style['webkitTransform'] = `scale(${scale})`
}
},

鼠标往下拉时,图片也跟着往下有一点距离,它是无缝的,计算scale的值为scale = 1 + percent。背景图片的高度(this.imageHeight)乘以scale就等于增加newY的高度,所以图片的高度和newY的滚动是相契合的。

图片跟随滚动高速模糊

当鼠标下拉时,希望图片有高速模糊的效果并且模糊效果随着图片向上滑时模糊度更大。定义变量blur,当newY>0时设置blur的比例值,然后设置bg-image的filter的样式属性backdrop-filter(backdrio-filter是css高速模糊的属性)。

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
watch: {
scrollY (newY) {
let translateY = Math.max(this.minTranslateY, newY)
let zIndex = 0
let scale = 1
let blur = 0
this.$refs.layer.style['transform'] = `translate3d(0,${translateY}px,0)`
this.$refs.layer.style['webkitTransform'] = `translate3d(0,${translateY}px,0)`
const percent = Math.abs(newY / this.imageHeight)
if (newY > 0) {
scale = 1 + percent
zIndex = 10
} else {
// 设置blur的值
blur = Math.min(20 * percent, 20)
}
// 设置filter的模糊度
this.$refs.filter.style['backdrop-filter'] = `blur(${blur}px)`
this.$refs.filter.style['webkitBackdrop-filter'] = `blur(${blur}px)`
// 当滚动到顶部时
if (newY < this.minTranslateY) {
zIndex = 10
this.$refs.bgImage.style.paddingTop = 0
this.$refs.bgImage.style.height = `${RESERVED_HEIGHT}px`
} else {
this.$refs.bgImage.style.paddingTop = '70%'
this.$refs.bgImage.style.height = 0
}
this.$refs.bgImage.style.zIndex = zIndex
this.$refs.bgImage.style['transform'] = `scale(${scale})`
this.$refs.bgImage.style['webkitTransform'] = `scale(${scale})`
}
},

实现返回键事件@click="back"

1
2
3
4
5
6
<div class="music-list">
<div class="back" @click="back">
<i class="icon-back"></i>
</div>
....
</div>

实现背景图片里的按钮

随机播放全部按钮

1
2
3
4
5
6
7
8
9
<div class="bg-image" :style="bgStyle" ref="bgImage">
<div class="play-wrapper">
<div class="play">
<i class="icon-play"></i>
<span class="text">随机播放全部</span>
</div>
</div>
<div class="filter" ref="filter"></div>
</div>

当数据加载完成时再显示按钮渲染。

1
<div class="play" v-show="songs.length>0">

当滚动到顶部时让随机播放全部按钮消失。

watch scrollY方法

1
2
3
4
5
6
7
8
9
10
11
// 当滚动到顶部时
if (newY < this.minTranslateY) {
zIndex = 10
this.$refs.bgImage.style.paddingTop = 0
this.$refs.bgImage.style.height = `${RESERVED_HEIGHT}px`
this.$refs.playBtn.style.display = 'none'
} else {
this.$refs.bgImage.style.paddingTop = '70%'
this.$refs.bgImage.style.height = 0
this.$refs.playBtn.style.display = ''
}

加载中效果

最后在scroll组件里注册loading组件

1
2
3
<div class="loading-container" v-show="!songs.length">
<loading></loading>
</div>