[Vue音乐项目] 第九节 歌手页面(点击跳转+滑动联动)

上一节完成了页面的展示及滚动效果,接下来实现歌手列表和歌手索引的联动效果,点击右侧的字母,中间跳转到相应的歌手位置,滑动歌手页面时,右侧的字母列表也随之改变。

  1. 打开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)
        }
    }
    
  2. 实现两边联动效果

    <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>
    
posted @ 2020-10-22 21:54  绝弹笔记  阅读(219)  评论(0编辑  收藏  举报