制作过程遇到的难点:
- 制作全屏背景图片:播放页面的背景图片使用了高斯模糊,由于到css的一样样式不太了解,制作起来耗费了一些事件。背景图片的高斯模糊使用了filter样式:
filter:blur(20px)
。在查阅资料的时候,发现了filter还有一些强大的功能,就是我们国家公祭日的时候,有些网站就会变成了灰色,就是用了filter这个样式:filter: grayscale(100%);
。有兴趣的可以去了解一下。 - 制作中间区域:因为中间区域是需要了装两个div的,一个是歌曲的图片,另一个是歌词。刚开始的时候准备用定位来实现的,发现不行,苦于没法子,就看了源码,才知道是通过
inline-block
和no-wrap
来实现的。 - 制作顶部和底部过渡效果:使用transition标签来制作过渡效果的时候,发现加了样式却没有过渡效果。研究了一下才发现,你不仅要对目标元素添加trasition样式,还要为
v-enter-active
和v-leave-active
添加transition样式,否则没有过渡效果。
收获的成果:
1.区分过渡和动画:过渡效果需要特定事件的触发,比如样式的切换等等。而动画是可以自由触发。动画效果可以设置每一步的状态,而过渡效果只能设置起点和终点。当使用原点作为起点的时候,应该用钩子函数enter来实现过渡效果,过渡完会触发trasitionend事件,用dom.addEventListener('transitionend',()=>{})
。如果用起点作为终点,在v-enter中设置起点。
2.css中的width和height:一般比较大的就使用百分比就表示,小的就用px来表示。
效果如下:
自己写的页面样式代码:
<template>
<div class="player" v-show="playList.length !== 0">
<transition
name="normal"
appear
@enter="enter"
@after-enter="afterEnter"
@leave="leave"
@after-leave="afterLeave"
>
<div class="normal-player" v-show="fullScreen">
<div class="background">
<img
src="https://y.gtimg.cn/music/photo_new/T002R300x300M000003y8dsH2wBHlo.jpg?max_age=2592000"
alt
/>
</div>
<div class="header">
<div class="close" @click="hide">
<i class="iconfont icon-fanhui"></i>
</div>
<h1 class="title">演员</h1>
<h2 class="singer">薛之谦</h2>
</div>
<div class="middle">
<div class="middle-l">
<div class="cd-wrapper" ref="cdWrapper">
<img
src="https://y.gtimg.cn/music/photo_new/T002R300x300M000003y8dsH2wBHlo.jpg?max_age=2592000"
alt
/>
</div>
<div class="playing-lyric-wrapper">
<div class="playing-lyric">演员 (Performer) - 薛之谦 (Joker)</div>
</div>
</div>
<div class="middle-r"></div>
</div>
<div class="bottom">
<div class="dot-wrapper">
<div class="dot dot-active"></div>
<div class="dot"></div>
</div>
<div class="progress-wrapper">
<div class="start">0:00</div>
<div class="progress">
<div class="progress-line"></div>
<div class="progress-active"></div>
<div class="progress-btn"></div>
</div>
<div class="end">4:21</div>
</div>
<div class="operators">
<div class="mode">
<i class="iconfont icon-xunhuan"></i>
</div>
<div class="prev">
<i class="iconfont icon-shangyishou_huaban"></i>
</div>
<div class="play-state">
<i class="iconfont icon-bofang"></i>
</div>
<div class="next">
<i class="iconfont icon-xiayishou_huaban"></i>
</div>
<div class="collect">
<i class="iconfont icon-xihuan"></i>
</div>
</div>
</div>
</div>
</transition>
<div class="mini-player" v-show="!fullScreen" @click="show">
<div class="icon">
<img
src="https://y.gtimg.cn/music/photo_new/T002R300x300M000003y8dsH2wBHlo.jpg?max_age=2592000"
alt
height="40"
width="40"
/>
</div>
<div class="text">
<h1 class="song-name">演员</h1>
<p class="singer-name">薛之谦</p>
</div>
<div class="control">
<i class="iconfont icon-bofang"></i>
</div>
<div class="control">
<i class="iconfont icon-caidan"></i>
</div>
</div>
<audio src></audio>
</div>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
import animations from 'create-keyframe-animation'
export default {
data () {
return {
}
},
computed: {
...mapGetters([
'playList',
'fullScreen'
])
},
components: {
},
methods: {
hide () {
this.setFullScreen(false)
},
show () {
this.setFullScreen(true)
},
enter (el, done) {
const { x, y, scale } = this._getPosAndScale()
const animation = {
0: {
transform: `translate3d(${x}px,${y}px,0) scale(${scale})`
},
60: {
transform: 'translate3d(0,0,0) scale(1.1)'
},
100: {
transform: 'translate3d(0,0,0) scale(1)'
}
}
animations.registerAnimation({
name: 'move',
animation,
presets: {
duration: 400,
easing: 'linear'
}
})
animations.runAnimation(this.$refs.cdWrapper, 'move', done)
},
afterEnter () {
animations.unregisterAnimation('move')
this.$refs.cdWrapper.style.animation = ''
},
leave (el, done) {
const { x, y, scale } = this._getPosAndScale()
this.$refs.cdWrapper.style.transition = 'all .4s'
this.$refs.cdWrapper.style.transform = `translate3d(${x}px,${y}px,0) scale(${scale})`
this.$refs.cdWrapper.addEventListener('transitionend', done)
},
afterLeave () {
this.$refs.cdWrapper.style.transition = ''
this.$refs.cdWrapper.style.transform = ''
},
_getPosAndScale () {
const paddingTop = 80
const posX = document.body.clientWidth * 0.5
const posY = (paddingTop + document.body.clientWidth * 0.8) / 2
const x2 = 40
const y2 = document.body.clientHeight - 30
const x = x2 - posX
const y = posY + y2
const scale = 40 / (document.body.clientWidth * 0.8)
return { x, y, scale }
},
...mapMutations({
setFullScreen: 'SET_FULL_SCREEN'
})
}
}
</script>
<style scoped lang="less">
.player {
width: 0;
height: 0;
.normal-player {
z-index: 999;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #222222;
.background {
position: absolute;
z-index: -1;
width: 100%;
height: 100%;
opacity: 0.6;
-webkit-filter: blur(20px);
filter: blur(20px);
img {
height: 100%;
width: 100%;
}
}
.header {
position: relative;
width: 100%;
height: 60px;
margin-bottom: 25px;
.close {
position: absolute;
top: 0;
left: 6px;
padding: 5px 10px;
transform: rotate(-90deg);
i {
color: #ffcd32;
font-size: 26px;
}
}
.title {
height: 40px;
text-align: center;
font-size: 18px;
color: white;
font-weight: 500;
line-height: 40px;
}
.singer {
height: 20px;
text-align: center;
font-size: 15px;
color: white;
line-height: 20px;
font-weight: 500;
}
}
.middle {
position: fixed;
width: 100%;
top: 80px;
bottom: 170px;
white-space: nowrap;
.middle-l {
display: inline-block;
position: relative;
padding-top: 80%;
width: 100%;
height: 0;
vertical-align: top;
.cd-wrapper {
position: absolute;
top: 0;
left: 10%;
width: 80%;
height: 100%;
img {
width: 100%;
height: 100%;
box-sizing: border-box;
border-radius: 50%;
border: 10px solid #979797;
}
}
.playing-lyric-wrapper {
margin: 30px 36px 0 36px;
text-align: center;
font-size: 14px;
color: hsla(0, 0%, 100%, 0.5);
}
}
.middle-r {
display: inline-block;
position: relative;
padding-top: 80%;
width: 100%;
height: 0;
}
}
.bottom {
position: absolute;
bottom: 50px;
width: 100%;
.dot-wrapper {
display: flex;
justify-content: center;
.dot {
width: 8px;
height: 8px;
margin-right: 8px;
border-radius: 50%;
background-color: #aaa9a9;
}
.dot-active {
width: 20px;
background-color: #dddddd;
border-radius: 8px;
}
}
.progress-wrapper {
display: flex;
justify-content: center;
height: 30px;
margin: 0 36px;
padding: 10px 0;
color: #dddddd;
font-size: 12px;
line-height: 30px;
.progress {
position: relative;
flex: 1;
margin: 0 10px;
.progress-line {
position: absolute;
top: 50%;
width: 100%;
height: 4px;
transform: translateY(-50%);
background-color: #656565;
}
.progress-active {
position: absolute;
top: 50%;
width: 1%;
height: 4px;
transform: translateY(-50%);
background-color: #ffcd32;
}
.progress-btn {
position: absolute;
top: 50%;
left: 0;
width: 16px;
height: 16px;
transform: translateY(-50%);
border: 3px solid white;
border-radius: 50%;
box-sizing: border-box;
background-color: #ffcd32;
}
}
}
.operators {
display: flex;
margin: 0 36px;
i {
font-size: 34px;
color: #ffcd32;
}
div {
flex: 1;
line-height: 46px;
text-align: center;
}
.play-state {
padding: 0 10px;
i {
font-size: 46px;
}
}
.mode {
text-align: left;
}
.collect {
text-align: right;
i {
color: white;
}
}
.mode {
i {
font-size: 20px;
}
}
}
}
}
.mini-player {
z-index: 999;
display: flex;
position: fixed;
bottom: 0;
right: 0;
left: 0;
height: 40px;
background-color: #333333;
padding: 10px 0;
.icon {
padding: 0 10px 0 20px;
img {
border-radius: 50%;
}
}
.text {
flex: 1;
.song-name {
font-weight: 500;
color: white;
font-size: 15px;
}
.singer-name {
font-size: 13px;
color: #8a8a8a;
}
}
.control {
padding-right: 10px;
margin-left: 10px;
i {
font-size: 35px;
color: #c5a231;
}
}
}
}
.normal-enter,
.normal-leave-to {
opacity: 0;
.header {
transform: translate3d(0, -100%, 0);
}
.bottom {
transform: translate3d(0, 100%, 0);
}
}
.normal-enter-active,
.normal-leave-active {
transition: all 0.4s;
.header {
transition: all 0.4s cubic-bezier(0.86, 0.18, 0.82, 1.32);
}
.bottom {
transition: all 0.4s cubic-bezier(0.86, 0.18, 0.82, 1.32);
}
}
</style>