肯德基宅急送,你值得学习的滚动屏

在移动端的滚动屏效果里面,肯德基的效果确实不错。原生的是用vue写的,这里我用react强行撸了个效果细节几乎一致的。vue

技术栈


react + antd + better-scroll + react-lazyloadreact

better-scroll:也许你并不陌生,是一款重点解决移动端(已支持 PC)各类滚动场景需求的插件,来自于黄轶老师。滚动场景在开发中常常碰见,有必要去掌握它。git

个人项目效果展现:


左右联动,效果贼棒。滚动也是如丝般顺滑。github

关于实现:


不知道原做是怎么实现的,这里我用react去实现它走了很多弯路。 这里简单的分析这个滚动屏效果的需求吧:数组

1. 点击左侧导航栏:bash

  • 点击的块有active指示(即有背景图)
  • 右侧菜单栏顶部标题更换。
  • 右侧菜单栏把对于的商品区块置顶
  • 左侧导航栏点击的该项也进行置顶(若是是最底部的那几个就不要动)

2. 右侧滚动商品项时,若是商品项的标题被右侧菜单栏顶部标题掩盖时antd

  • 左侧导航栏对应的项目也对应置顶(若是是最底部的那几个就不动,切换active便可)

OK 需求分析完毕。函数

实现方案
基本思想:ui

  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里面,实时监听,这样就产生了一个问题,当点击左侧导航栏的时候,右侧在滚动这样又将调用右侧滚轮监听函数而后执行。这样二者就会矛盾冲突,这里的解决办法是: 设置一个开关变量,若是点击左侧按钮时,开关打开,在右侧滚动结束时再把开关关闭,这样在开关打开时,右侧滚轮监听函数不执行。this

结语:


最后,真挚的感谢你的阅读

项目地址: github.com/g00d-mornin…

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

相关文章
相关标签/搜索