这个横向菜单知道什么可见

一个基于浏览器自身滚动的 React 滚动菜单——逐项可见性追踪、箭头、拖拽,以及完整的命令式 API。gzip 后 5.7 kB

Sci-Fi
Comedy
Drama
Horror
Docs
Kids
Action
Indie
Anime
Classics
Thriller
Reality

getVisible() → ['scifi', 'comedy', 'drama', 'horror', 'docs', 'kids']

This is the library, live — drag it. Dimmed tiles are the ones useIsVisible reports as off-screen.

npm install react-horizontal-scrolling-menu每月下载 34.7 万5.7 kB 压缩+gzipReact 16.8 – 19MIT

自动播放,无需轮播引擎

没有 autoplay 属性——这条轨道是公开 API 上的一道配方:把行克隆到两端、在接缝处跳转一次 scrollLeft,再用一个定时器调用 scrollNext()。它在悬停、聚焦及隐藏的标签页下暂停,在减少动态效果偏好下保持静止——你甚至可以跨接缝反向拖拽它。

Tokyo
Oslo
Lima
Cairo
Sydney
Quito
Seoul
Porto
Denver
Hanoi

菜单,不是轮播

Embla、Swiper 和 keen-slider 用 JavaScript 重新实现滚动来构建图片滑块——吸附点、弹簧物理、渲染循环。本库不提供其中任何一样。它依托浏览器原生滚动,并加上浏览器无法提供的那一样东西:确切知道哪些项目在屏幕上。

对全屏图片滑块而言是 错误的工具——那里请用 Embla 或 Swiper。对分类栏、标签页条、筛选标签,以及任何你的应用需要感知的一行内容,它则是 正确的工具

原生滚动

惯性、滚动条、触摸、滚轮与无障碍都来自浏览器,而非物理引擎。这一行在你的 JavaScript hydrate 之前就能滚动——本页每个演示都是服务端渲染的。

可见性追踪

IntersectionObserver 报告哪些项目在屏幕上。useIsVisible(itemId) 让一个组件订阅一个项目——无需滚动位置计算,并且只有受影响的那些项目会重新渲染。

需要时命令式

scrollToItemscrollNextscrollPrev、按 id 或索引查找——通过菜单内部的 context,或来自外部的 apiRef

你的组件,你的 CSS

箭头、header、footer 与每个项目都是你编写的组件。项目宽度是你的 CSS。本库只附带 210 字节的布局样式,绝不碍事。

快速开始

一个文件,零配置:带 itemId 的项目、读取 VisibilityContext 的两个箭头,以及样式表导入。

App.tsx
import React from 'react';
import {
  ScrollMenu,
  VisibilityContext,
  type publicApiType,
} from 'react-horizontal-scrolling-menu';
import 'react-horizontal-scrolling-menu/dist/styles.css';

const items = Array.from({ length: 10 }, (_, i) => `item-${i + 1}`);

export function App() {
  return (
    <ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>
      {items.map((id) => (
        <Card itemId={id} key={id} title={id} />
      ))}
    </ScrollMenu>
  );
}

function LeftArrow() {
  const visibility = React.useContext<publicApiType>(VisibilityContext);
  const isFirstVisible = visibility.useIsVisible('first', true);
  return (
    <button
      disabled={isFirstVisible}
      onClick={() => visibility.scrollPrev()}
    >

    </button>
  );
}

function RightArrow() {
  const visibility = React.useContext<publicApiType>(VisibilityContext);
  const isLastVisible = visibility.useIsVisible('last', false);
  return (
    <button
      disabled={isLastVisible}
      onClick={() => visibility.scrollNext()}
    >

    </button>
  );
}

function Card({ itemId, title }: { itemId: string; title: string }) {
  const visibility = React.useContext<publicApiType>(VisibilityContext);
  const isVisible = visibility.useIsVisible(itemId);
  return (
    <div className="card" data-visible={isVisible}>
      <div>{title}</div>
      <div>visible: {String(isVisible)}</div>
    </div>
  );
}

The code on the left, running:

item-1
visible: true
item-2
visible: true
item-3
visible: true
item-4
visible: false
item-5
visible: false
item-6
visible: false
item-7
visible: false
item-8
visible: false
item-9
visible: false
item-10
visible: false

每个项目都必须有 itemId——追踪正是靠它。React 的 key 作为后备方案。

styles.css 是单独的导入;JS 包绝不会注入 CSS。

项目宽度来自你自己的 CSS——菜单不做任何测量。

阅读完整的快速入门示例

或者交给你的编程代理

基于旧版本训练的模型仍会伸手去要 visibleElementsSeparator 项目和一个 Arrows 属性——这些多年前就已移除——并凭空捏造一个从未存在过的 autoplay 属性。本包随附八个 SKILL.md 文件来阻止这种情况:按任务划分的指导,你的代理通过 TanStack Intent 按需加载,随库一起发布版本,而不随本页更新。

npx @tanstack/intent@latest install

在已安装该包的项目里运行一次。你的代理随即会从 node_modules/react-horizontal-scrolling-menu/skills/ 发现这些技能。

  • menu-setup第一个可用的菜单、箭头、必需的 CSS 导入
  • menu-visibility屏幕上有什么,以及两端的箭头状态
  • menu-scrollingscrollToItem、apiRef、一次一页的分页
  • menu-interactions拖拽、滚轮与触摸——以及它们的事件处理工厂
  • menu-recipes自动播放、无限循环、加载更多:是配方,不是属性
  • menu-transitions-rtl动画时长、自定义缓动、从右到左
  • menu-testing-ssrNext.js 与 RSC、Jest 模拟、Playwright
  • menu-migration升级 v8 之前的代码,以及模型仍在凭空捏造的 API

盒子里有什么

  • 逐项可见性 hook——useIsVisible(itemId)
  • 用于箭头状态的 first / last 辅助函数
  • scrollToItem · scrollNext · scrollPrev
  • 用于从菜单外部控制的 apiRef
  • 拖拽、滚轮、触摸与滚动条输入
  • 动态增删检测
  • Header 与 Footer 插槽
  • slidingWindow + getItemsPos 分页辅助函数
  • 从右到左支持
  • 自定义过渡函数
  • SSR 安全——本页就是证明
  • TypeScript 优先——导出 publicApiType
  • 在 React 16.8 – 19 之间保持一套稳定的 API

盒子里没有的

  • 吸附与弹簧物理
  • 全屏图片滑块
  • 灯箱

那些属于图片滑块的领域——Embla 和 Swiper 做得很好。无限循环自动播放 也不是属性——它们是配方:每个都在公开 API 上约六十行,可在 Storybook 中实时编辑。本页顶部那条轨道正是这个配方在运行。这里始终是个菜单。

上个月被约 20,000 个仓库下载了 347,516 次——自 2018 年维护至今。

GitHub 上 788 颗星登上 React Status #257Our World in Data 中生产使用

每个示例都可以在浏览器里编辑

这个 Storybook 同时也是一个沙盒:每个 story 都附带一个加载了库的真实类型定义的 Monaco 编辑器。改代码,看它重新渲染——无需沙盒账号,也无需本地搭建。

由 Aleksandr Smyshliaev 构建并维护

2018 年首次发布,在 React 16.8 到 19 之间保持相同的公开 API。Aleksandr 是一名前端工程师——React、Next.js、TypeScript——目前接受外包与全职工作。