React实现菜单栏滚动功能

 更新时间:2024年03月25日 11:02:47   作者:卡卡舅舅  
本文将会基于react实现滚动菜单栏功能,点击菜单,内容区域会自动滚动到对应卡片,内容区域滑动,指定菜单栏会被选中,代码简单易懂,感兴趣的朋友一起看看吧
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

简介

        本文将会基于react实现滚动菜单栏功能。

技术实现

实现效果

       点击菜单,内容区域会自动滚动到对应卡片。内容区域滑动,指定菜单栏会被选中。

ScrollMenu.js

import {useRef, useState} from "react";
import './ScrollMenu.css';
export const ScrollMenu = ({products}) => {
    // 获取 categoryProductMap
    const categoryProductMap = new Map();
    products.forEach(product => {
        const category = product.category;
        let categoryProductList = categoryProductMap.get(category);
        if (!categoryProductList) {
            categoryProductList = [];
        }
        categoryProductList.push(product);
        categoryProductMap.set(category, categoryProductList);
    });
    // 获取类别列表
    const categoryList = Array.from(categoryProductMap.keys());
    // 菜单选中索引
    const [current, setCurrent] = useState(0);
    /**
     * 内容引用
     */
    const contentRef = useRef();
    /**
     * 当左侧菜单点击时候
     */
    const onMenuClick = (idx) => {
        if (idx !== current) {
            // 内容自动滚动到对应菜单位置
            contentRef.current.scrollTop = height.slice(0, idx).reduce((a, b) => a + b, 0);
            setCurrent(idx);
        }
    }
    /**
     *  计算右侧商品类别卡片高度
     */
    const height = [];
    const itemHeight = 25;
    categoryList.forEach((category, index) => {
        var productCnt = categoryProductMap.get(category).length;
        height.push((productCnt + 1) * itemHeight); // 0.8 是header高度
    });
    console.log(height)
    /**
     * 当右侧内容滚动时候
     */
    const onContentScroll = () => {
        const scrollTop = contentRef.current.scrollTop;
        if (current < height.length - 1){
            const nextIdx = current + 1;
            // 计算下一个位置高度
            const nextHeight = height.slice(0, nextIdx).reduce((a, b) => a + b, 0);
            console.log('scrollTop', scrollTop, 'nextHeight', nextHeight, 'nextIdx', nextIdx)
            if (scrollTop >= nextHeight) {
                contentRef.current.scrollTop = nextHeight;
                setCurrent(nextIdx);
                return;
            }
        }
        if (current > 0) {
            const lastIdx = current - 1;
            // 计算上一个位置高度
            const lastHeight = height.slice(0, lastIdx).reduce((a, b) => a + b, 0);
            console.log('scrollTop', scrollTop, 'lastHeight', lastHeight, 'lastIdx', lastIdx)
            if (scrollTop <= lastHeight) {
                contentRef.current.scrollTop = lastHeight;
                setCurrent(lastIdx);
                return;
            }
        }
    }
    return (
        <div className='scroll-menu'>
            <div className='menu'>
                {
                    // 菜单列表
                    categoryList.map((category, index) => {
                        return (
                            <div className={"menu-item" + ((index === current )? '-active' : '')}
                                 key={`${index}`} id={`menu-item-${index}`}
                                 onClick={(event) => {
                                     onMenuClick(index)
                                 }}>
                                {category}
                            </div>
                        )
                    })
                }
            </div>
            <div className='content' ref={contentRef} onScroll={(event) => {
                onContentScroll()
            }}>
                {
                    categoryList.map((category, index) => {
                        // 获取类别商品
                        const productList = categoryProductMap.get(category);
                        return (
                            <div key={index}>
                                <div className='content-item-header' key={`${index}`}
                                     id={`content-item-${index}`} style={{
                                    height: itemHeight
                                }} >{category}</div>
                                {
                                    productList.map((product,idx) => {
                                        return <div className='content-item-product'style={{
                                            height: itemHeight
                                        }}  key={`${index}-${idx}`} >{product.name}</div>
                                    })
                                }
                            </div>
                        )
                    })
                }
            </div>
        </div>
    )
}

ScrollMenu.css

.scroll-menu {
    display: flex;
    flex-direction: row;
    width: 300px;
    height: 100px;
}
.menu{
    width: 90px;
    height: 100px;
    display: flex;
    flex-direction: column;
}
.menu-item {
    text-align: center;
    vertical-align: middle;
}
.menu-item-active {
    text-align: center;
    vertical-align: middle;
    background-color: lightcoral;
}
.content {
    width: 210px;
    overflow: auto;
}
.content-item-header{
    text-align: left;
    vertical-align: top;
    background-color: lightblue;
}
.content-item-product{
    text-align: center;
    vertical-align: center;
    background-color: lightyellow;
}

App.js

import './App.css';
import {ScrollMenu} from "./component/scroll-menu/ScrollMenu";
const App = ()=> {
    const products = [
        {
            category:'蔬菜',
            name:'辣椒'
        },
        {
            category:'蔬菜',
            name:'毛豆'
        },
        {
            category:'蔬菜',
            name:'芹菜'
        },
        {
            category:'蔬菜',
            name:'青菜'
        },
        {
            category:'水果',
            name:'苹果'
        },
        {
            category:'水果',
            name:'梨'
        },
        {
            category:'水果',
            name:'橘子'
        },   {
            category:'食物',
            name:'肉'
        },   {
            category:'食物',
            name:'罐頭'
        }
        ,   {
            category:'食物',
            name:'雞腿'
        }
    ];
    return (
        <ScrollMenu products={products}/>
    )
}
export default App;

到此这篇关于React实现菜单栏滚动的文章就介绍到这了,更多相关React 菜单栏滚动内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • 详解react-router如何实现按需加载

    详解react-router如何实现按需加载

    本篇文章主要介绍了react-router如何实现按需加载,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • 教你快速搭建 React Native 开发环境

    教你快速搭建 React Native 开发环境

    这篇文章主要介绍了搭建 React Native 开发环境的详细过程,本文通过图文指令给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • React Redux应用示例详解

    React Redux应用示例详解

    这篇文章主要介绍了如何在React中直接使用Redux,目前redux在react中使用是最多的,所以我们需要将之前编写的redux代码,融入到react当中去,本文给大家详细讲解,需要的朋友可以参考下
    2022-11-11
  • React Native自定义控件底部抽屉菜单的示例

    React Native自定义控件底部抽屉菜单的示例

    本篇文章主要介绍了React Native自定义控件底部抽屉菜单的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-02-02
  • React组件实例三大核心属性State props Refs详解

    React组件实例三大核心属性State props Refs详解

    组件实例的三大核心属性是:State、Props、Refs。类组件中这三大属性都存在。函数式组件中访问不到 this,也就不存在组件实例这种说法,但由于它的特殊性(函数可以接收参数),所以存在Props这种属性
    2022-12-12
  • React Native使用百度Echarts显示图表的示例代码

    React Native使用百度Echarts显示图表的示例代码

    本篇文章主要介绍了React Native使用百度Echarts显示图表的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • React Fiber与调和深入分析

    React Fiber与调和深入分析

    Fiber可以理解为一个执行单元,每次执行完一个执行单元,React Fiber就会检查还剩多少时间,如果没有时间则将控制权让出去,然后由浏览器执行渲染操作,这篇文章主要介绍了React Fiber架构原理剖析,需要的朋友可以参考下
    2022-11-11
  • React路由中的redux和redux知识点拓展

    React路由中的redux和redux知识点拓展

    这篇文章主要介绍了React路由中的redux和redux知识点拓展,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的朋友可以参考学习一下
    2022-08-08
  • react使用axios进行api网络请求的封装方法详解

    react使用axios进行api网络请求的封装方法详解

    这篇文章主要为大家详细介绍了react使用axios进行api网络请求的封装方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • react如何实现筛选条件组件

    react如何实现筛选条件组件

    这篇文章主要介绍了react如何实现筛选条件组件问题,具有很好的参考价价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10

最新评论

?


http://www.vxiaotou.com