在移動端的滾動屏效果裡面,肯德基的效果確實不錯。原生的是用vue寫的,這裡我用react強行擼了個效果細節幾乎一致的。
技術棧
react + antd + better-scroll + react-lazyload
better-scroll:也許你並不陌生,是一款重點解決移動端(已支援 PC)各種滾動場景需求的外掛,來自於黃軼老師。滾動場景在開發中經常遇見,有必要去掌握它。
我的專案效果展示:
左右聯動,效果賊棒。滾動也是如絲般順滑。
關於實現:
不知道原作是怎麼實現的,這裡我用react去實現它走了不少彎路。 這裡簡單的分析這個滾動屏效果的需求吧:
1. 點選左側導航欄:
- 點選的塊有active指示(即有背景圖)
- 右側選單欄頂部標題更換。
- 右側選單欄把對於的商品區塊置頂
- 左側導航欄點選的該項也進行置頂(如果是最底部的那幾個就不要動)
2. 右側滾動商品項時,如果商品項的標題被右側選單欄頂部標題掩蓋時
- 左側導航欄對應的專案也對應置頂(如果是最底部的那幾個就不動,切換active即可)
OK 需求分析完畢。
實現方案:
基本思想:
- 通過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 })
}
複製程式碼
- 然後就是根據功能分離函式
// 監聽右側滾輪 如果數值達到就改變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();
})
})
}
複製程式碼
- 剩下的就是一些關於元件通訊,你需要掌握子元件給父元件通訊,父元件呼叫子元件方法等。
備註: 關於右側滾輪監聽,監聽是放在Scroll元件裡面的onScroll裡面,實時監聽,這樣就產生了一個問題,當點選左側導航欄的時候,右側在滾動這樣又將呼叫右側滾輪監聽函式然後執行。這樣兩者就會矛盾衝突,這裡的解決辦法是: 設定一個開關變數,如果點選左側按鈕時,開關開啟,在右側滾動結束時再把開關關閉,這樣在開關開啟時,右側滾輪監聽函式不執行。
結語:
最後,真摯的感謝你的閱讀
專案地址: github.com/g00d-mornin…
better-scroll文件: ustbhuangyi.github.io/better-scro…