1. 程式人生 > 實用技巧 >[Vue音樂專案] 第九節 歌手頁面(點選跳轉+滑動聯動)

[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>