在移动端的滚动屏效果里面,肯德基的效果确实不错。原生的是用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…