---
title: "React 中的 Netflix 风格横向滚动行"
description: "使用原生滚动，在 React 中构建 Netflix 风格的分类行：悬停显示箭头、边缘渐隐、拖拽滚动、可见性跟踪。附带实时演示和完整源码。"
canonical: "https://react-horizontal-scrolling-menu.dev/zh-cn/netflix-row"
image: "https://react-horizontal-scrolling-menu.dev/og.png"
---

# React 中的 Netflix 风格横向滚动行

你在每个流媒体网站上滑动浏览的那一排海报，其实不是轮播图——而是一个菜单。它不会吸附对齐，也不会自动播放；它依靠原生的惯性滚动，箭头则叠加在上层。这正是 `react-horizontal-scrolling-menu` 提供的能力：你的卡片、原生滚动，以及逐项可见性检测，让箭头知道何时该隐藏。

Sci-Fi

Comedy

Drama

Horror

Docs

Kids

Action

Indie

Anime

Classics

Thriller

Reality

拖拽它，或者悬停在这一行上——箭头会在边缘处渐显，当到达该行的相应端点时，对应箭头就会消失。

[在 Storybook 中实时编辑此示例](https://asmyshlyaev177.github.io/react-horizontal-scrolling-menu/?path=/story/examples-mousedrag--mouse-drag)

## 为什么这不是轮播图该做的事

Netflix 风格的行从不会一次只显示一张幻灯片。边缘处的项目故意被部分裁切——被裁切的海报正是在提示“还有更多”。轮播引擎则与此背道而驰：它们用 JavaScript 变换接管手势层，吸附到幻灯片边界，并重新实现用户浏览器本就具备的惯性滚动。对于一排可点击的卡片来说，这些都是多余的开销。

原生滚动免费为你提供了惯性、触摸、触控板和滚动条支持。它没有提供的两样东西是叠加箭头和感知屏幕上有哪些卡片——而这正是本库通过逐项 [`useIsVisible`](https://react-horizontal-scrolling-menu.dev/zh-cn/examples/simple.md) 和边缘感知箭头状态所补上的两件事。

## 成就这种效果的三个细节

-   **箭头叠加在内容之上**，而不是并排放置。将它们绝对定位在行的两端渲染（上方演示中它们通过 `Header` 传入，以便保留在菜单的上下文内），悬停时显示，并在 [`useLeftArrowVisible` / `useRightArrowVisible`](https://react-horizontal-scrolling-menu.dev/zh-cn/examples/simple.md) 报告该端已到达时将对应箭头隐藏。
-   **边缘渐隐。** 只需一行 CSS——在滚动容器上加一个 `mask-image` 渐变——就替代了轮播插件为此专门实现的“窥视”逻辑。
-   **拖拽不能触发点击。** 鼠标拖拽如果结束在某张海报上，不应打开它。[拖拽滚动方案](https://react-horizontal-scrolling-menu.dev/zh-cn/examples/mouse-drag.md) 会跟踪拖拽状态，并精准吞掉那一次点击。

## 扩展规模：懒加载的行与超长的栏

流媒体界面会堆叠数十行、数百张卡片。由于每个项目都只是原生滚动容器内的普通 DOM，滚动时不会触发任何重新渲染——[性能示例](https://react-horizontal-scrolling-menu.dev/zh-cn/examples/performance.md) 在不做虚拟化的情况下也能运行 300 个项目。逐项可见性检测还能免费带来图片懒加载：在 `useIsVisible` 报告卡片进入屏幕之前，先渲染一个占位符。

如果你的行需要在末尾循环回绕，这正是幻灯片语义真正有用的地方——在转向轮播引擎之前，可以先看看 [无限循环方案](https://react-horizontal-scrolling-menu.dev/zh-cn/examples/infinite-loop.md) 中约 60 行的用户态实现。

## 最简模式

在原生滚动行上叠加箭头——上方演示就是这个结构再加上样式。下方的 shadcn 组件提供了包含拖拽和边缘渐隐效果的完整可直接使用的源码。

MediaRow.tsx

```
function MediaRow({ children }: { children: React.ReactNode }) {
  return (
    <div className="group relative">
      <ScrollMenu Header={<OverlayArrows />} wrapperClassName="edge-fade">
        {children}
      </ScrollMenu>
    </div>
  );
}

// Rendered as Header, so it lives inside the menu's VisibilityContext
// and can sit absolutely positioned over the row ends.
function OverlayArrows() {
  const api = React.useContext<publicApiType>(VisibilityContext);
  const atStart = api.useLeftArrowVisible();
  const atEnd = api.useRightArrowVisible();
  return (
    <>
      <button
        className="overlay-arrow left-0"
        hidden={atStart}
        onClick={() => api.scrollPrev()}
      >
        ←
      </button>
      <button
        className="overlay-arrow right-0"
        hidden={atEnd}
        onClick={() => api.scrollNext()}
      >
        →
      </button>
    </>
  );
}

/* .edge-fade — the Netflix edge, one CSS line:
   mask-image: linear-gradient(to right,
     transparent, black 40px, black calc(100% - 40px), transparent); */
```

## 或者以 shadcn 组件的形式安装

[media-row](https://react-horizontal-scrolling-menu.dev/r/media-row.json) 注册表条目正是这个模式——悬停箭头、渐变边缘渐隐、拖拽滚动——以 Tailwind 样式组件的形式安装进你的 `components/ui/`，可自由编辑：

shadcn

```
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/media-row.json
```

## 相关示例

-   [用鼠标拖拽滚动鼠标拖拽的同时不破坏项目点击。](https://react-horizontal-scrolling-menu.dev/zh-cn/examples/mouse-drag.md)
-   [无限循环来自公开 API 的无缝循环——无需改动库。](https://react-horizontal-scrolling-menu.dev/zh-cn/examples/infinite-loop.md)
-   [5000 个项目依然流畅原生滚动可扩展——这里无需虚拟化。](https://react-horizontal-scrolling-menu.dev/zh-cn/examples/performance.md)

[全部 21 个示例](https://react-horizontal-scrolling-menu.dev/zh-cn/examples.md)

---

More examples: <https://react-horizontal-scrolling-menu.dev/examples.md>
Library summary for LLMs: <https://react-horizontal-scrolling-menu.dev/llms.txt>
