# 滚动出现 Scroll Reveal > 内容滚进屏幕时才淡入、往上滑一点,只出现一次。 分类:动效 · 角色:基础(手感,哪里都能用、不抢眼) [全部组件和流程](https://www.dachuiai.com/llms.txt) · [动效选型规则](https://www.dachuiai.com/guides/motion.md) ## 给 AI:用户把这个组件发给了你 1. 先看用户的项目:框架、页面类型、现在的风格、手机还是电脑为主。 2. 对照下面的「适合 / 不适合」判断合不合适。不合适就直接说,并从[动效选型规则](https://www.dachuiai.com/guides/motion.md)或[全部组件](https://www.dachuiai.com/llms.txt)里另挑一个。 3. 合适就按「装进项目」装好,示例里的文字和图片换成用户自己的。 4. 告诉用户用了什么、放在哪个文件、以后想改快慢、颜色、大小改哪个参数。 ## 什么时候用 - 适合:几乎所有展示型页面的段落、卡片和图片。 - 不适合:首屏(一打开就该看到);后台和表单;文章正文每一段都加(打断阅读)。 - 适合的网站:官网、电商、作品集、SaaS、内容站、活动页、本地小店 - 放在:内容区、列表、卡片 - 手机上:好用:和电脑上一样。 - 性能代价:低:浏览器自带的 IntersectionObserver。 - 常搭配:[错峰 Stagger](https://www.dachuiai.com/c/stagger.md)、[缓动 Easing](https://www.dachuiai.com/c/easing.md) ## 装进项目 - 写法:React 18 以上 + TypeScript + Tailwind CSS v4,一个文件就能用,不引用别的组件。 - 依赖:`npm i lucide-react`(lucide-react 只有最后的演示示例用到;不留示例的话,删掉对应的 import 就不用装) - 文件:原样存成 `scroll-reveal.tsx`(比如 `src/components/scroll-reveal.tsx`)。能跑命令就下载[原文](https://www.dachuiai.com/code/scroll-reveal.tsx),比手抄准:`curl -fsSL -o src/components/scroll-reveal.tsx https://www.dachuiai.com/code/scroll-reveal.tsx`(Windows 写 `curl.exe`)。下载完看一眼第一行是 `import`,不是网页。 - 导出:`Reveal` 是可以直接用的部分;最后的 `export default` 是演示示例,只作参考。 - 图片:示例用了本站的 [/art/forest.jpg](https://www.dachuiai.com/art/forest.jpg)、[/art/bokeh.jpg](https://www.dachuiai.com/art/bokeh.jpg)、[/art/lava.jpg](https://www.dachuiai.com/art/lava.jpg)、[/art/sunset.jpg](https://www.dachuiai.com/art/sunset.jpg)、[/art/silk-amber.jpg](https://www.dachuiai.com/art/silk-amber.jpg)、[/art/ice.jpg](https://www.dachuiai.com/art/ice.jpg)。换成用户自己的图;只想先看效果,可以从本站同样的路径下载,放进项目的 public 目录。 - 注意:用 Reveal 包住要出现的内容;同一排要依次出现时传 delay={(i % 列数) * 60},列数随屏幕变时按最大列数算。时长写在文件里 .sr 样式中:淡入 .6s、上滑 .7s、位移 28px。 - 保留代码里 `prefers-reduced-motion` 的处理:用户开了「减弱动态效果」时内容照样完整显示。 - Next.js(有 app 文件夹):文件第一行加 `'use client';`。 - 用 JavaScript 不用 TypeScript:Vite 和 Next.js 都能直接用 .tsx 文件;也可以删掉类型标注,改名 .jsx。 - 没有 Tailwind:先装 Tailwind CSS v4(Vite 项目 `npm i tailwindcss @tailwindcss/vite`,vite 配置的 plugins 里加 `tailwindcss()`,入口引入的那个 CSS 文件(一般是 `src/index.css`)第一行写 `@import "tailwindcss";`),或者把类名改写成项目用的样式。 - 已经装了 Tailwind v3:别为了一个组件升级。类名大多通用,少数 v4 写法要换,比如焦点框 `outline-2` 在 v3 里要写成 `outline outline-2`。 - 不是 React(Vue、Svelte、小程序、纯 HTML):照着代码的做法改写,时长、曲线、参数保持一样。Wix、有赞这类建站平台装不进代码,告诉用户去平台自带的动效设置里找。 ## 参数 | 参数 | 默认值 | 作用 | | --- | --- | --- | | `children` | | 包在里面的内容 | | `root` | | 滚动盒子的 ref;整页滚动时不传 | | `delay` | `0` | 延迟多久才开始(毫秒) | | `className` | `''` | 加在最外层的类名,用来调大小、位置、间距 | ## 你可以说 > 文章列表加 Scroll Reveal 滚动出现:每一篇滚进屏幕时才淡入、往上滑一点,只出现一次。 ## 完整代码 ```tsx import {useEffect, useRef, useState, type ReactNode, type RefObject} from 'react'; import {Clock} from 'lucide-react'; /* --------------------------------------------------------------- * ScrollReveal 滚动出现 * 内容滚进屏幕的时候才淡入、往上滑一点,而不是一打开就全摆在那。 * 只出现一次;往回滚不会再藏起来。 * 整页用时 root 不传;放在可滚动的盒子里时,传这个盒子。 * ------------------------------------------------------------- */ export function Reveal({children, root, delay = 0, className = ''}: {children: ReactNode; root?: RefObject; delay?: number; className?: string}) { const ref = useRef(null); const [shown, setShown] = useState(false); useEffect(() => { const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setShown(true); io.disconnect(); } }, {root: root?.current ?? null, threshold: 0.2, rootMargin: '0px 0px -8% 0px'}); if (ref.current) io.observe(ref.current); return () => io.disconnect(); }, [root]); return (
{children}
); } /* ---------------- 示例:文章列表 ---------------- */ const POSTS = [ ['春日露营装备清单', '户外', 5, '/art/forest.jpg', '从帐篷到睡袋,第一次露营需要准备的 12 件东西。'], ['城市夜跑路线推荐', '运动', 3, '/art/bokeh.jpg', '五条沿江路线,路灯亮、人不多、补给方便。'], ['一个人的晚餐', '生活', 4, '/art/lava.jpg', '十五分钟能做好的六道快手菜。'], ['周末短途旅行', '旅行', 6, '/art/sunset.jpg', '高铁两小时以内,适合周五下班就出发的小城。'], ['新手咖啡入门', '生活', 5, '/art/silk-amber.jpg', '手冲、法压、挂耳,先从哪一种开始。'], ['极简书桌布置', '家居', 4, '/art/ice.jpg', '留下五样东西,桌面就清爽了。'], ] as const; export default function ScrollRevealDemo() { const box = useRef(null); return (

本周精选

6 篇新文章

{POSTS.map(([title, tag, min, img, text]) => (
{tag}

{title}

{text}

{min} 分钟阅读
))}
); } ```