上一节完成了页面的展示及滚动效果,接下来实现歌手列表和歌手索引的联动效果,点击右侧的字母,中间跳转到相应的歌手位置,滑动歌手页面时,右侧的字母列表也随之改变。
-
打开src/base/listview/index.vue(没有则创建),实现点击跳转
//实现原理: 因为字母列表索引和歌手列表相对应,在触屏开始的时候,记录下开 //始索引index及当前元素位于浏览器器的 //Top偏移值y1;在触屏移动的时候,记录下当前top偏移值y2,根据(y2-y1)/元素高度 //得到偏移索引,开始索引index+偏移索引等于当前索引。把当前 //索引传递进_scrollTo, <template> <ul> <li ref="grouplist"> <h2> ... </h2> <ul> <li ref="itemlist"> </ul> </li> </ul> //添加两个事件监听,1.触屏开始 2.触屏移动 <div class="list-shortcut" @touchStart="onTS" @touchMove.top.prevent="onTM"> </div> </template> <script> export default { created() { this.touch = {}, } methods: { //[1] 为索引列表添加触屏开始事件处理程序 onTS(e) { //获取开始li元素的data_index属性 let index = getData(e.target,'index') //获取开始li元素的偏移值 this.touch.y1 = e.touchs[0].pageY //放置当前索引 this.touch.index = index //[3] 歌手列表滚动到指定位置 this._scrollTo(index) }, //[2] 为索引列表添加触屏移动事件处理程序 onTM(e) { //保存当前li元素的偏移值 this.touch.y2 = e.touchs[0].pageY //获取偏移索引 let offset = (this.touch.y2-this.touch.y1) / 18 | 0 //计算得出当前索引 let index = parseInt(this.touch.index) + offset //[3] 歌手列表滚动到指定位置 this._scrollTo(index) }, //[方法] 歌手列表滚动到指定位置 _scrollTo(index) { //对index做预先处理 ps: heights最后两个高度是多余的 if(!index && index!=0) return if(index < 0) { index = 0 } else if(index > this.heights.length - 2) { index = this.heights.length - 2 } //滚动歌手列表 ps:方法scrollToElement在Scroll组件定义 this.$refs.listview.scrollToElement(this.$refs.grouplist[index],0) } }, } </script> //scr/base/scroll/index.vue,methods添加以下方法 scrollToElement() { this.scroll && this.scroll.scrollToElement.apply(this.scroll,arguments) } //scr/common/js/dom.js 添加以下内容 export function getData(el,name,value) { const prefix = 'data-' name = prefix + name if(value) { return el.setAttribute(name,value) } else { return el.getAttribute(name) } }
-
实现两边联动效果
<template> //[2.1] 滚动时,添加监听函数 <m-scroll @scroll="scroll" > <ul> <li> <h2> ... </h2> <ul> //[3] 动态改变右侧字母索引的样式 <li :class="{'current': currentIndex == index}" > </ul> </li> </ul> <div> </div> //[fixed 1](共三步) 歌手列表固定栏 通过fixed变量动态改变 <div class="list-fixed" v-show="fixedTitle" ref="fixed"> <div class="fixed-title">{{fixedTitle}}</div> </div> </scroll> </template> <script> export default { computed: { //[fixed 2] 计算是否固定 fixed() { if(this.scrollY > 0) return undefined return this.data[this.currentIndex] ? this.data[this.currentIndex].title : undefined } }, methods: { //如果手动点击,更新歌手列表的ScrollY值 scroll() { ... this.scrollY = -this.heightList[index] }, _calculateHeight() { var height = 0 this.heightList.push(height) for(let i=0; i<this.$refs.listgroup.length; i++) { height += this.$refs.listgroup[i].clientHeight this.heightList.push(height) } }, //[2.2] 监听处理函数,赋值给scrollY scroll(pos) { this.scrollY = pos.y } }, watch: { data() { //[1] 获取歌手列表各分组高度 this._calculateHeight() }, //[3] scrollY变化时,处理得到currentIndex scrollY(newValue) { if(newY > 0) { this.currentIndex = 0 return } for(let i=0; i<this.heightList.length; i++) { let height1 = this.heightList[i] let height2 = this.heightList[i+1] if(-newY >= height1 && -newY < height2) { this.currentIndex = i [fixed 3] 监听父组到顶部的距离,固定栏减少该段距离 this.diff = height2 + newY return } } this.currentIndex = this.heightList.length - 2 } }, //实现固定栏逐渐减少高度的动画,平滑过渡 diff(newVal) { let fixedTop = (newVal>0 && newVal<30) ? newVal - 30 : 0 if(this.fixedTop == fixedTop) return this.fiexedTop = fixedTop this.$refs.fixed.style.transform = `translate3d(0,${fixedTop}px,0)` } } </script>