平滑滚动 Smooth Scroll
用 Lenis 为页面或自定义容器提供惯性平滑滚动基座。
基础用法
左右两栏内容相同:左侧为浏览器原生滚动,右侧由 SmoothScroll 接管。用滚轮分别滚动,对比惯性与停顿的手感;右栏顶部的进度条通过 useLenis 读取实例进度。
pnpm dlx @wui-design/cli@latest add @wui/smooth-scroll"use client"
import * as React from "react"
import { ReactLenis, type LenisProps } from "lenis/react"
import type { LenisOptions } from "lenis"
export interface SmoothScrollProps extends Omit<LenisProps, "options"> {
/** Content rendered inside the Lenis context or custom scroll container. */
children?: React.ReactNode
/** Lenis instance options. */
options?: LenisOptions
/** Disable interpolation while preserving the content wrapper. @default false */
disabled?: boolean
/** Respect the operating system reduced-motion preference. @default true */
respectReducedMotion?: boolean
}
/** Provides Lenis smooth scrolling for the document or a custom container. */
function SmoothScroll({
children,
root = true,
options,
disabled = false,
respectReducedMotion = true,
className,
...props
}: SmoothScrollProps) {
const [reduceMotion, setReduceMotion] = React.useState(false)
React.useEffect(() => {
if (!respectReducedMotion) return
const media = window.matchMedia("(prefers-reduced-motion: reduce)")
const update = () => setReduceMotion(media.matches)
update()
media.addEventListener("change", update)
return () => media.removeEventListener("change", update)
}, [respectReducedMotion])
if (disabled || reduceMotion) {
return root ? (
<>{children}</>
) : (
<div className={className} {...props}>
{children}
</div>
)
}
return (
<ReactLenis
root={root}
className={className}
options={{ autoRaf: true, ...options }}
{...props}
>
{children}
</ReactLenis>
)
}
export { SmoothScroll }
使用场景
SmoothScroll 封装 Lenis 的 React Provider。默认 root 模式接管文档滚动;设置 root={false} 可以创建独立滚动容器。
options 会直接传给 Lenis,并默认开启 autoRaf。页面级平滑滚动和局部内容滚动应分别选择一种,不要重复包裹同一滚动区域。
子组件可以通过 lenis/react 导出的 useLenis 订阅滚动,例如驱动阅读进度:
import { useLenis } from "lenis/react"
import { motion, useMotionValue } from "motion/react"
function Progress() {
const progress = useMotionValue(0)
useLenis((lenis) => progress.set(lenis.progress))
return <motion.div className="h-0.5 origin-left bg-foreground" style={{ scaleX: progress }} />
}按照 Lenis 官方建议,在应用入口引入配套样式:
import "lenis/dist/lenis.css"属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| children | ReactNode | — | 要放入 Lenis 上下文或自定义滚动容器中的内容。 |
| options | LenisOptions | — | Lenis 实例的配置。 |
| disabled | boolean | false | 是否停用插值滚动,但保留内容包装结构。 |
| respectReducedMotion | boolean | true | 是否遵循操作系统的减少动态效果偏好。 |
| root | boolean | "asChild" | true | 是否在页面上初始化全局 Lenis 实例。 |
| autoRaf | boolean | true | 是否自动使用 requestAnimationFrame 驱动 Lenis。 |
使用说明
平滑滚动应作为页面级能力只挂载一次,不要在普通卡片中层层嵌套。模态框、代码编辑器和需要原生滚动的区域可使用 Lenis 的
data-lenis-prevent 属性。组件会尊重用户的“减少动态效果”偏好,此时保留内容并停止滚动插值,恢复为普通滚动行为。
Lenis 的完整选项、推荐 CSS 和集成方式请参考官方 React 文档。