在移動端的滾動屏效果裏面,肯德基的效果確實不錯。原生的是用vue寫的,這裏我用react強行擼了個效果細節幾乎一致的。vue
react + antd + better-scroll + react-lazyloadreact
better-scroll:也許你並不陌生,是一款重點解決移動端(已支持 PC)各類滾動場景需求的插件,來自於黃軼老師。滾動場景在開發中常常碰見,有必要去掌握它。git
左右聯動,效果賊棒。滾動也是如絲般順滑。github
不知道原做是怎麼實現的,這裏我用react去實現它走了很多彎路。 這裏簡單的分析這個滾動屏效果的需求吧:數組
1. 點擊左側導航欄:bash
- 點擊的塊有active指示(即有背景圖)
- 右側菜單欄頂部標題更換。
- 右側菜單欄把對於的商品區塊置頂
- 左側導航欄點擊的該項也進行置頂(若是是最底部的那幾個就不要動)
2. 右側滾動商品項時,若是商品項的標題被右側菜單欄頂部標題掩蓋時antd
- 左側導航欄對應的項目也對應置頂(若是是最底部的那幾個就不動,切換active便可)
OK 需求分析完畢。函數
實現方案:
基本思想:ui
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裏面,實時監聽,這樣就產生了一個問題,當點擊左側導航欄的時候,右側在滾動這樣又將調用右側滾輪監聽函數而後執行。這樣二者就會矛盾衝突,這裏的解決辦法是: 設置一個開關變量,若是點擊左側按鈕時,開關打開,在右側滾動結束時再把開關關閉,這樣在開關打開時,右側滾輪監聽函數不執行。this
最後,真摯的感謝你的閱讀
項目地址: github.com/g00d-mornin…
better-scroll文檔: ustbhuangyi.github.io/better-scro…