手动实现一个锚点导航(react)

大致的场景如下图,点击右侧导购跳转相对应得模块,简单记录一下遇到的问题以及最后的实现办法


1677141083544.jpg

一、实现点击跳转
最开始是用的scrollIntoView来实现跳转,确实用法简单且自带滑动效果,方法如下:

    const jumpHeight = (id, index) => {
        setActiveIndex(index)
        document.getElementById(id).scrollIntoView({
            behavior: "smooth"
        })
    }

但是会造成头部被遮盖滚动不下来的问题,后面改用scrollTop实现,getBoundingClientRect()可获取DOM元素到浏览器可视范围的距离,如下

    const jumpHeight = (id, index) => {
        setActiveIndex(index)
        let scrollTop = document.documentElement.scrollTop + 
       document.getElementById(id).getBoundingClientRect().top
        document.documentElement.scrollTop = scrollTop
    }

二、实现滑动滚动条高亮锚点
一开始使用getBoundingClientRect()的top高度来计算对应的高亮,如下

 const scrollChange = () => {
        let part1 = document.getElementById('baseBox').getBoundingClientRect() 
        let part2 = document.getElementById('riskBox').getBoundingClientRect()
        let part3 = document.getElementById('reliabilityBox').getBoundingClientRect() 
        if (part1.top === 0) {
            setActiveIndex(0)
        } else if (part2.top <= 25 && part2.top >= 1) {
            setActiveIndex(1)
        } else (part3.top <= 30 && part3.top >= 10) {
            setActiveIndex(2)
        }
}

随后发现从上往下滑动没问题,但是从下往上滑动会有不精确的问题,所以后面改用IntersectionObserver实现,如下

        //找到所有的模块节点,放到数组
        const boxList = navigationList.map(item => document.getElementById(item.id))
        // 开始监听
        let arr = []
        var io = new IntersectionObserver((entries) => {
            entries.forEach((i, index) => {
                arr.push({ intersectionRatio: i.intersectionRatio, index: index })
            })
            // 节点露出比例排序
            arr.sort((a, b) => a.intersectionRatio < b.intersectionRatio ? 1 : a.intersectionRatio > b.intersectionRatio ? -1 : 0)
            setActiveIndex(arr[0].index)//高亮该锚点
        }, {
            root: null,
            threshold: 1, // 阀值设为1,当只有比例达到1时才触发回调函数
        })
        // observe遍历监听所有节点
        boxList.forEach(box => io.observe(box))

三、完整代码

//导航栏
const NavigationBar = () => {
    const [activeIndex, setActiveIndex] = useState(0)
    const navigationList = [
        {
            img: require('../../img/flow_navigation_icons_1.png'),
            text: '基本信息',
            id: 'baseBox',
        },
        {
            img: require('../../img/flow_navigation_icons_2.png'),
            text: '风险结论',
            id: 'riskBox'
        },
        {
            img: require('../../img/flow_navigation_icons_3.png'),
            text: '可信度分析',
            id: 'reliabilityBox'
        },
        {
            img: require('../../img/flow_navigation_icons_4.png'),
            text: '流水评估',
            id: 'flowBox'
        },
        {
            img: require('../../img/flow_navigation_icons_5.png'),
            text: '风险提示',
            id: 'tipBox'
        },
        {
            img: require('../../img/flow_navigation_icons_6.png'),
            text: '营销反哺',
            id: 'marketBox'
        }
    ]
    useEffect(() => {
        window.addEventListener('scroll', scrollChange)
    }, [])
    // 窗口滚动事件
    const scrollChange = () => {
        //找到所有的模块节点,放到数组
        const boxList = navigationList.map(item => document.getElementById(item.id))
        // 开始监听
        let arr = []
        var io = new IntersectionObserver((entries) => {
            entries.forEach((i, index) => {
                arr.push({ intersectionRatio: i.intersectionRatio, index: index })
            })
            // 节点露出比例排序
            arr.sort((a, b) => a.intersectionRatio < b.intersectionRatio ? 1 : a.intersectionRatio > b.intersectionRatio ? -1 : 0)
            setActiveIndex(arr[0].index)//高亮该锚点
        }, {
            root: null,
            threshold: 1, // 阀值设为1,当只有比例达到1时才触发回调函数
        })
        // observe遍历监听所有节点
        boxList.forEach(box => io.observe(box))
    }
    // 跳转页面高度
    const jumpHeight = (id, index) => {
        setActiveIndex(index)
        let scrollTop = document.documentElement.scrollTop + document.getElementById(id).getBoundingClientRect().top
        document.documentElement.scrollTop = scrollTop
    }
    return (
        <div className="page-navigation">
            {
                navigationList.map((item, index) => {
                    return (
                        <div className="navigation-box" key={item.id} onClick={() => { jumpHeight(item.id, index) }}>
                            <img src={item.img} alt="" />
                            <span className={activeIndex === index ? 'active' : ''}>{item.text}</span>
                        </div>
                    )
                })
            }
        </div >
    )
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容