在singer.vue中,点击某一歌手,根据路由跳转到singer-detail.vue在vue-muscie:singer.vue页面的5.数据序列化后传入list-vue中相关逻辑
vue
selectSinger(singer) { //导航至singer-detail.vue页面 this.$router.push({ path: `/singer/${singer.id}` }) //同时,将singer的信息放入vuex中 this.setSinger(singer) },
因为singer-detail.vue页面具备共同性,所以封装成一个组件music-list.vueweb
这个组件比较简单,一个壳子,获取数据传入组件vuex
_getDetail() { //若是在歌手详情页面刷新,没法获取singer.id,则直接返回歌手列表页面 if (!this.singer.id) { this.$router.push('/singer') } getSingerDetail(this.singer.id).then((res) => { if (res.code === ERR_OK) { this.songs = this._normallizeSongs(res.data.list) } }) },
_normallizeSongs(list) { let ret = [] list.forEach((item) => { let {musicData} = item if (musicData.songid && musicData.albummid) { ret.push(createSong(musicData)) } }) return ret }
这里使用createSong,建立一个song实例浏览器
这个组件需求:app
组件须要接收的数据props函数
props: { bgImage: { //背景图片 type: String, default: '' }, songs: { //歌手详情数据songs type: Array, default: () => [] }, title: { //标题title type: String, default: '' }, }
背景图片的利用:style 和 computedthis
<div class="bg-image" :style="bgStyle" ref="bgImage"> computed: { bgStyle() { return `background-image:url(${this.bgImage})` } },
根据歌手查询到歌曲列表,须要滚动scroll.vueurl
<div class="bg-layer" ref="layer"></div> <scroll @scroll="scroll" //须要派发滚动位置,触发的事件 :probe-type="probeType" //不须要截流 :listen-scroll="listenScroll" //派发滚动位置 :data="songs" //接收数据,初始化scroll class="list" ref="list"> <div class="song-list-wrapper"> <song-list @select="selectItem" :songs="songs" :rank="rank"></song-list> </div> </scroll>
mounted() { //背景图图片的高度 this.imageHeight = this.$refs.bgImage.clientHeight //滚动的边界:滚动到此 背景图片 ——(减去) 顶部title的高度 this.minTranslateY = -this.imageHeight + RESERVED_HEIGHT(40) //初始化的时候列表设置top值:为了列表向上滚动而进行traslate3d this.$refs.list.$el.style.top = `${this.$refs.bgImage.clientHeight}px` },
触发滚动事件spa
scroll(pos) { this.scrollY = pos.y },
监听滚动位置scrollY3d
watch: { scrollY(newY) { //往上滑动newY是负数 translateY往上移动是负数 //没有滑到临界值的时候translateY=newY 滑动顶部40的时候translateY=this.minTranslateY 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)` let zIndex = 0 //缩放 let scale = 1 //比例 let perscent = Math.abs(newY / this.imageHeight) //高斯模糊 let blur = 0 //下拉的时候 if (newY > 0) { scale = 1 + perscent zIndex = 10 } else { //上滑的时候:高斯模糊 blur = Math.min(20 * perscent, 20) } if (newY < this.minTranslateY) { //上滑 `过` 临界值的时候,背景图片相应改变,按钮消失 zIndex = 10 this.$refs.bgImage.style.paddingTop = 0 this.$refs.bgImage.style.height = `${RESERVED_HEIGHT}px` this.$refs.playButton.style.display = 'none' } else { //下滑 `未过` 临界值的时候 this.$refs.bgImage.style.paddingTop = '70%' this.$refs.bgImage.style.height = '0' this.$refs.playButton.style.display = '' } this.$refs.bgImage.style.zIndex = zIndex //为何从顶部显示scale缩放:由于下面的CSS .bg-image中 transform-origin: top //下拉的时候放大效果 this.$refs.bgImage.style[transform] = `scale(${scale})` //高斯模糊 IOS this.$refs.bgImage.style[backdrop] = `blur(${blur}px)` } },
在使用transform以及back-drop的时候,须要根绝浏览器添加一些前缀,封装成方法调用
let elementStyle = document.createElement('div').style //自执行函数:浏览器兼容的前缀 let vender = (() => { const tranforNames = { webkit:'webkitTransfrom', Moz:'MozTransfrom', O:'OTransfrom', ms:'msTransfrom', standard:'standard' } for(let key in transforNames){ if(elementStyle[transfromNames[key]] !== undefined){ return key } } return false })() export function prefixStyle(style){ if(vendor === false){ return false } if(ventor === 'standard'){ return style } return vendor + style.charAt(0).toUpperCase() + style.substr(1) }
在music-list.vue中引入
const transform = prefixStyle('transform')
列表很常见,封装成组件song-list.vue
传入songs数据
功能:展现数据,传出点击的数据
<div class="song-list"> <ul> <li @click="selectItem(song,index)" :key="song.id" v-for="(song,index) in songs" class="item"> <div class="content"> <h2 class="name">{{song.name}}</h2> <p class="desc">{{getDesc(song)}}</p> </div> </li> </ul> </div> methods:{ getDesc(song) { return `${song.singer} . ${song.album}` }, selectItem(item, index) { this.$emit('select', item, index) }, }