React 中的 Netflix 风格横向滚动行
你在每个流媒体网站上滑动浏览的那一排海报,其实不是轮播图——而是一个菜单。它不会吸附对齐,也不会自动播放;它依靠原生的惯性滚动,箭头则叠加在上层。这正是 react-horizontal-scrolling-menu 提供的能力:你的卡片、原生滚动,以及逐项可见性检测,让箭头知道何时该隐藏。
拖拽它,或者悬停在这一行上——箭头会在边缘处渐显,当到达该行的相应端点时,对应箭头就会消失。
为什么这不是轮播图该做的事
Netflix 风格的行从不会一次只显示一张幻灯片。边缘处的项目故意被部分裁切——被裁切的海报正是在提示“还有更多”。轮播引擎则与此背道而驰:它们用 JavaScript 变换接管手势层,吸附到幻灯片边界,并重新实现用户浏览器本就具备的惯性滚动。对于一排可点击的卡片来说,这些都是多余的开销。
原生滚动免费为你提供了惯性、触摸、触控板和滚动条支持。它没有提供的两样东西是叠加箭头和感知屏幕上有哪些卡片——而这正是本库通过逐项 useIsVisible 和边缘感知箭头状态所补上的两件事。
成就这种效果的三个细节
- 箭头叠加在内容之上,而不是并排放置。将它们绝对定位在行的两端渲染(上方演示中它们通过
Header传入,以便保留在菜单的上下文内),悬停时显示,并在useLeftArrowVisible/useRightArrowVisible报告该端已到达时将对应箭头隐藏。 - 边缘渐隐。 只需一行 CSS——在滚动容器上加一个
mask-image渐变——就替代了轮播插件为此专门实现的“窥视”逻辑。 - 拖拽不能触发点击。 鼠标拖拽如果结束在某张海报上,不应打开它。拖拽滚动方案 会跟踪拖拽状态,并精准吞掉那一次点击。
扩展规模:懒加载的行与超长的栏
流媒体界面会堆叠数十行、数百张卡片。由于每个项目都只是原生滚动容器内的普通 DOM,滚动时不会触发任何重新渲染——性能示例 在不做虚拟化的情况下也能运行 300 个项目。逐项可见性检测还能免费带来图片懒加载:在 useIsVisible 报告卡片进入屏幕之前,先渲染一个占位符。
如果你的行需要在末尾循环回绕,这正是幻灯片语义真正有用的地方——在转向轮播引擎之前,可以先看看 无限循环方案 中约 60 行的用户态实现。
最简模式
在原生滚动行上叠加箭头——上方演示就是这个结构再加上样式。下方的 shadcn 组件提供了包含拖拽和边缘渐隐效果的完整可直接使用的源码。
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 注册表条目正是这个模式——悬停箭头、渐变边缘渐隐、拖拽滚动——以 Tailwind 样式组件的形式安装进你的 components/ui/,可自由编辑:
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/media-row.json