React - 实现菜单栏滚动
简介
本文将会基于react实现滚动菜单栏功能。
技术实现
实现效果

点击菜单,内容区域会自动滚动到对应卡片。内容区域滑动,指定菜单栏会被选中。
ScrollMenu.js
import {useRef, useState} from "react";
import './ScrollMenu.css';export const ScrollMenu = ({products}) => {// 获取 categoryProductMapconst 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实现滚动菜单栏功能。 技术实现 实现效果 点击菜单,内容区域会自动滚动到对应卡片。内容区域滑动,指定菜单栏会被选中。 ScrollMenu.js import {useRef, useState} from "react"; import ./ScrollMenu.css;export co…...
线性筛选(欧拉筛选)-洛谷P3383
#include <bits/stdc.h> using namespace std; int main() {std::ios::sync_with_stdio(false); cin.tie(nullptr); //为了加速int n, q;cin >> n >> q; vector<int>num(n 1); //定义数字表vector<int>prime; //定义素数表数组num[1] …...
企业微信可以更换公司主体吗?
企业微信变更主体有什么作用?当我们的企业因为各种原因需要注销或已经注销,或者运营变更等情况,企业微信无法继续使用原主体继续使用时,可以申请企业主体变更,变更为新的主体。企业微信变更主体的条件有哪些࿱…...
Qt教程 — 3.6 深入了解Qt 控件:Display Widgets部件(2)
目录 1 Display Widgets简介 2 如何使用Display Widgets部件 2.1 QTextBrowser组件-简单的文本浏览器 2.2 QGraphicsView组件-简单的图像浏览器 Display Widgets将分为两篇文章介绍 文章1(Qt教程 — 3.5 深入了解Qt 控件:Display Widgets部件-CSDN…...
Golang案例开发之gopacket抓包三次握手四次分手(3)
文章目录 前言一、理论知识三次握手四次分手二、代码实践1.模拟客户端和服务器端2.三次握手代码3.四次分手代码验证代码完整代码总结前言 TCP通讯的三次握手和四次分手,有很多文章都在介绍了,当我们了解了gopacket这个工具的时候,我们当然是用代码实践一下,我们的理论。本…...
如何减少pdf的文件大小?pdf压缩工具介绍
文件发不出去,有时就会耽误工作进度,文件太大无法发送,这应该是大家在发送PDF时,常常会碰到的问题吧,那么PDF文档压缩大小怎么做呢?因此我们需要对pdf压缩后再发送,那么有没有好用的pdf压缩工具…...
TypeScript基础类型
string、number、bolean 直接在变量后面添加即可。 let myName: string Tomfunction sayHello(person: string) {return hello, person } let user Tom let array [1, 2, 3] console.log(sayHello(user))function greet(person: string, date: Date): string {console.lo…...
长安链智能合约标准协议第二草案——BNS与DID协议邀请社区用户评审
长安链智能合约标准协议 在智能合约编写过程中,不同的产品及开发人员对业务理解和编程习惯不同,即使同一业务所编写的合约在具体实现上也可能有很大差异,在运维或业务对接中面临较大的学习和理解成本,现有公链合约协议规范又不能完…...
安防监控视频汇聚平台EasyCVR接入海康Ehome设备,设备在线但视频无法播放是什么原因?
安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…...
【Python + Django】表结构创建
以员工管理系统为例。 事前呢,我们先把项目和app创建出来,详细步骤可以看我同栏目的第一篇、第二篇文章。 我知道你们是不会下来找的,就把链接贴在下面吧: 【Python Django】启动简单的文本页面-CSDN博客 【Python Django】…...
解锁编程潜能:ChatGPT如何革新软件开发
目录 一、背景 二、功能描述 三、总结 一、背景 在这个飞速发展的数字时代,软件开发的效率和质量成了衡量一个开发者能力的重要标准。随着人工智能技术的不断进步,越来越多的开发者开始寻找能够提升工作效率的新方法。我就是其中之一,最近…...
内网使用rustdesk进行远程协助
文章目录 前言一、搭建rustdesk中继服务器二、搭建文件下载服务器三、创建引导脚本四、使用 前言 内网没有互联网环境,没法使用互联网上有中继服务器的远程协助工具,如teamviewer、todesk、向日癸等;在内网进行远程维护可以自己搭建中继服务…...
linux内核input子系统概述
目录 一、input子系统二、关键数据结构和api2.1 数据结构2.1.1 input_dev2.1.2 input_handler2.1.3 input_event2.1.4 input_handle 2.2 api接口2.2.1 input_device 相关接口input_device 注册流程事件上报 2.2.2 input handle 相关接口注册 handle指定 handle 2.2.3 input han…...
【解决报错】vi/vim修改文件时报错:Found a swap file by the name xxxxx
目录 报错内容报错原因解决方法 报错内容 vim打开文件提示: Found a swap file by the name xxxxx报错原因 使用vi或vim编辑器编写代码时由于网络不稳定(或其他种种原因)断开了连接,编辑好的代码没有运行和保存,再次…...
BRAM底层原理详细解释(1)
目录 一、原语 二、端口简述 2.1 端口简介 2.2 SDP端口映射 三、端口信号含义补充说明 3.1 字节写使能(Byte-Write Enable)- WEA and WEBWE: 3.2 地址总线—ADDRARDADDR and ADDRBWRADDR 3.3 数据总线—DIADI, DIPADIP, DIBDI, and D…...
GEE:为什么在机器学习分类或回归时,提取特征变量后的样本点下载到本地时,数据为空且缺少坐标?
作者:CSDN @ _养乐多_ 在博客《GEE:随机森林分类教程(样本制作、特征添加、训练、精度、参数优化、贡献度、统计面积)》和《GEE:随机森林回归预测教程(样本点、特征添加、训练、精度、参数优化、贡献度)》中,详细记录了在 Google Earth Engine(GEE)平台上进行机器学…...
电脑安装双系统windows和ubuntu server
1.创建Ubuntu-server的启动盘 首先要从官网下载Ubuntu-server18.04的ISO文件,用rufs烧录到U盘。如下所示 2. 磁盘分区 在windows创建两个盘(linuxboot 和linuxroot),后面一个一个用于boot,一个用于root. 3.开机U盘启…...
掌握这8大工具,自媒体ai写作之路畅通无阻! #媒体#媒体
在当今信息爆炸的时代,写作成为了人们表达思想、分享知识和传递情感的重要方式之一。对于很多人来说,写作并非易事。我们会陷入困境,无法找到灵感,我们会苦恼于语言表达的准确性,还有时候我们可能遭遇到了创作瓶颈&…...
「渗透笔记」致远OA A8 status.jsp 信息泄露POC批量验证
前言部分 在本节中,我会分两部分来说明致远OA A8 status.jsp 信息泄露的验证问题,其实就是两种验证方式吧,都一样,都是批量验证,主要如下所示: 通过Python脚本进行批量验证,但是前提是你可以收…...
uni-app打包证书android
Android平台打包发布apk应用,需要使用数字证书(.keystore文件)进行签名,用于表明开发者身份。 Android证书的生成是自助和免费的,不需要审批或付费。 可以使用JRE环境中的keytool命令生成。 以下是windows平台生成证…...
AI超清画质增强实战:低分辨率图片修复,让模糊变清晰
AI超清画质增强实战:低分辨率图片修复,让模糊变清晰 1. 技术背景与核心价值 1.1 为什么需要超分辨率技术 在数字图像处理领域,我们经常遇到这样的困扰:珍贵的家庭老照片因年代久远变得模糊不清;从网络下载的图片分辨…...
从原理到实战:一文读懂主流交叉验证技术及其Python/R实现
1. 交叉验证的本质与价值 第一次听说"交叉验证"这个词时,我正被一个电商用户流失预测项目折磨得焦头烂额。当时在测试集上的准确率像过山车一样忽高忽低,直到 mentor 扔给我一句:"你该试试 K 折交叉验证"。这个简单的改变…...
OpenClaw安全实践:Qwen3.5-9B本地化处理敏感数据
OpenClaw安全实践:Qwen3.5-9B本地化处理敏感数据 1. 为什么金融从业者需要本地化AI助手 上个月我帮一位在投行工作的朋友分析季度财报时,遇到了一个典型困境:他们需要从上百页PDF中提取关键财务指标,但公司禁止使用任何第三方云…...
STM32 RTC掉电也能走时?手把手教你用VBAT和LSE晶振搭建硬件时钟电路
STM32 RTC掉电也能走时?手把手教你用VBAT和LSE晶振搭建硬件时钟电路 嵌入式系统中实时时钟(RTC)的重要性不言而喻,它不仅是记录时间的工具,更是许多关键功能的基石。想象一下,当你的智能门锁因为断电而无法…...
tmux 示例
技术文章大纲示例:人工智能在医疗诊断中的应用 引言 概述人工智能在医疗领域的重要性当前医疗诊断面临的挑战人工智能技术的引入如何改变传统诊断方式 人工智能技术基础 机器学习与深度学习的核心概念计算机视觉在医疗影像分析中的作用自然语言处理(NLP&…...
OAuth2.0令牌安全指南:在Postman中模拟令牌泄露与防御实验
OAuth2.0令牌攻防实战:Postman模拟三大泄露场景与高级防御策略 在API安全领域,OAuth2.0令牌就像数字世界的临时护照,一旦落入不法分子之手,攻击者就能以用户身份横行无阻。本文将带您深入三大典型令牌泄露场景的模拟实验ÿ…...
华为元老许映童下周敲钟:思格新能开启招股:估值超100亿美元 高瓴是基石
雷递网 雷建平 4月8日思格新能源(上海)股份有限公司(简称:“思格新能”,股票代码:“06656”)今日开启招股,准备2026年4月16日在港交所上市。思格新能计划发售1357.39万股,…...
PaddlePaddle-GPU环境配置:为什么你的显卡总是被识别成CPU?(附解决方案)
PaddlePaddle-GPU环境配置:为什么你的显卡总是被识别成CPU?(附解决方案) 刚拿到新显卡准备大展拳脚,却发现PaddlePaddle死活不认GPU,这种挫败感我太懂了。明明花大价钱买的显卡,结果深度学习训…...
从LTE到5G NR:OFDM采样率与带宽设计的演进与挑战
1. LTE时代的OFDM参数设计:稳定但受限的起点 记得我第一次调试LTE基站时,被30.72MHz这个神奇的数字困扰了很久。为什么偏偏是这个采样频率?这要从LTE的OFDM基础设计说起。在LTE系统中,子载波间隔被固定为15kHz,就像用尺…...
5步掌握抖音无水印下载终极指南:从零到批量处理高效方案
5步掌握抖音无水印下载终极指南:从零到批量处理高效方案 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback sup…...
