肯德基宅急送,你值得學習的滾動屏

good_morning發表於2019-02-15

在移動端的滾動屏效果裡面,肯德基的效果確實不錯。原生的是用vue寫的,這裡我用react強行擼了個效果細節幾乎一致的。

技術棧


react + antd + better-scroll + react-lazyload

better-scroll:也許你並不陌生,是一款重點解決移動端(已支援 PC)各種滾動場景需求的外掛,來自於黃軼老師。滾動場景在開發中經常遇見,有必要去掌握它。

我的專案效果展示:


左右聯動,效果賊棒。滾動也是如絲般順滑。

肯德基宅急送,你值得學習的滾動屏

關於實現:


不知道原作是怎麼實現的,這裡我用react去實現它走了不少彎路。 這裡簡單的分析這個滾動屏效果的需求吧:

1. 點選左側導航欄:

  • 點選的塊有active指示(即有背景圖)
  • 右側選單欄頂部標題更換。
  • 右側選單欄把對於的商品區塊置頂
  • 左側導航欄點選的該項也進行置頂(如果是最底部的那幾個就不要動)

2. 右側滾動商品項時,如果商品項的標題被右側選單欄頂部標題掩蓋時

  • 左側導航欄對應的專案也對應置頂(如果是最底部的那幾個就不動,切換active即可)

OK 需求分析完畢。

實現方案
基本思想:

  1. 通過ref獲取元件的TOP偏移量陣列,然後獲得的偏移量資料,可以通過Scroll元件中去呼叫scrollTo()這個API來達到置頂效果
componentDidMount() {
        //獲取左側導航欄的top資訊
        let leftScrollYList = [];
        let initLeftTop = this.leftRef.current.childNodes[0].getBoundingClientRect().top
        for (let item of this.leftRef.current.childNodes) {
            leftScrollYList.push(item.getBoundingClientRect().top - initLeftTop)
        }
        console.log('獲取的左側導航欄資訊---------')
        console.log(leftScrollYList)
        //獲取右側選單塊的top資訊
        let rightScrollYList = [];
        let initRightTop = this.rightRef.current.childNodes[0].getBoundingClientRect().top
        console.log(initRightTop)
        for (let item of this.rightRef.current.childNodes) {
            rightScrollYList.push(item.getBoundingClientRect().top - initRightTop + this.BLOCKTITLEHEIGHT)
        }
        console.log('獲取的右側導航欄資訊(處理後)---------')
        rightScrollYList = rightScrollYList.slice(0, -1)
        rightScrollYList[0] = rightScrollYList[0] - this.BLOCKTITLEHEIGHT;
        console.log(rightScrollYList)
        this.setState({ leftScrollYList, rightScrollYList })
    }
複製程式碼
  1. 然後就是根據功能分離函式
// 監聽右側滾輪 如果數值達到就改變leftCurrentIndex 
    onListenRightTop() {
        if(this.rightChildRef.state.disableRightListen) {
            return;
        }else {
            let currentIndex = this.rightChildRef.state.currentIndex;
            if (currentIndex !== this.state.leftCurrentIndex) {
                this.setState({ leftCurrentIndex: currentIndex }, () => {
                    this._syncBlockTitle();
                    this._shiftLeft()
                })
            }
        }  
    }
    // 讓右側商品標題與currentIndex同步
    _syncBlockTitle() {
        let { leftCurrentIndex, blockTitleList, currentBlockTitle } = this.state;
        if (blockTitleList[leftCurrentIndex] !== currentBlockTitle) {
            console.log(this.state.blockTitleList)
            this.setState({ currentBlockTitle: blockTitleList[leftCurrentIndex] })
        }
    }
    //左側進行滾動
    _shiftLeft() {
        let length = this.leftRef.current.childNodes.length;
        let lastBottom = this.leftRef.current.childNodes[length - 1].getBoundingClientRect().bottom;
        let currentOffsetTop = this.leftRef.current.childNodes[this.state.leftCurrentIndex].getBoundingClientRect().top;
        console.log('當前active元素的Top偏移移---------')
        console.log(currentOffsetTop);
        let leftScollY = this.state.leftScrollYList[this.state.leftCurrentIndex]
        if (lastBottom > this.state.windowHeight || currentOffsetTop < 300) {
            this.leftChildRef.scrollTo(leftScollY);
        }
    }
    //右側進行滾動
    _shiftRight() {
        console.log('右側的top漂移-------------------');
        let rightScollY = this.state.rightScrollYList[this.state.leftCurrentIndex]
        this.rightChildRef.scrollTo(rightScollY)
    }
    clickLeft(event) {
        //重構此方法  他的作用分離為只是獲得當前元素的key,偏移等方法分離出來
        event.preventDefault()
        let leftCurrentIndex = event.currentTarget.dataset.lkey;
        let currentBlockTitle = leftList[leftCurrentIndex].blockName;
        this.rightChildRef.setState({disableRightListen: true},() => {
            this.setState({
                leftCurrentIndex,
                currentBlockTitle,
            }, () => {
                this._shiftLeft();
                this._shiftRight();
            })
        })
    }
複製程式碼
  1. 剩下的就是一些關於元件通訊,你需要掌握子元件給父元件通訊,父元件呼叫子元件方法等。

備註: 關於右側滾輪監聽,監聽是放在Scroll元件裡面的onScroll裡面,實時監聽,這樣就產生了一個問題,當點選左側導航欄的時候,右側在滾動這樣又將呼叫右側滾輪監聽函式然後執行。這樣兩者就會矛盾衝突,這裡的解決辦法是: 設定一個開關變數,如果點選左側按鈕時,開關開啟,在右側滾動結束時再把開關關閉,這樣在開關開啟時,右側滾輪監聽函式不執行。

結語:


最後,真摯的感謝你的閱讀

專案地址: github.com/g00d-mornin…

better-scroll文件: ustbhuangyi.github.io/better-scro…

相關文章