# 视差滚动 Parallax > 往下滚时远处的层动得慢、近处的层动得快,看起来有纵深。 分类:动效 · 角色:主角(占一整块、抢全部注意力,一页最多一个) [全部组件和流程](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. 告诉用户用了什么、放在哪个文件、以后想改快慢、颜色、大小改哪个参数。 ## 什么时候用 - 适合:首屏有能分层的风景、插画或产品图(天空、远山、前景、文字)。 - 不适合:只有一张平面图(硬拆层很假);文字为主的页面;后台。 - 适合的网站:官网、活动页、作品集、电商 - 放在:首屏、背景 - 手机上:好用:层数减到两三层,位移幅度减半。 - 性能代价:低:几层 transform。 - 常搭配:[滚动出现 Scroll Reveal](https://www.dachuiai.com/c/scroll-reveal.md)、[速度跑马灯 Velocity Marquee](https://www.dachuiai.com/c/velocity-marquee.md) ## 装进项目 - 写法:React 18 以上 + TypeScript + Tailwind CSS v4,一个文件就能用,不引用别的组件。 - 依赖:只用 react,不用另外安装 - 文件:原样存成 `parallax.tsx`(比如 `src/components/parallax.tsx`)。能跑命令就下载[原文](https://www.dachuiai.com/code/parallax.tsx),比手抄准:`curl -fsSL -o src/components/parallax.tsx https://www.dachuiai.com/code/parallax.tsx`(Windows 写 `curl.exe`)。下载完看一眼第一行是 `import`,不是网页。 - 导出:`useScrollY`,是做这个效果用的工具(hook、函数或常量);页面结构在最后的 `export default` 示例里,照着示例搭。 - 图片:示例用了本站的 [/tex/s2.jpg](https://www.dachuiai.com/tex/s2.jpg)、[/tex/s6.jpg](https://www.dachuiai.com/tex/s6.jpg)、[/tex/s5.jpg](https://www.dachuiai.com/tex/s5.jpg)。换成用户自己的图;只想先看效果,可以从本站同样的路径下载,放进项目的 public 目录。 - 注意:导出的只有读滚动距离的 useScrollY,分层的结构在示例里,照着示例搭:每层 translateY(滚动距离 × 速度),速度越小越远。放在首屏最合适,因为滚动距离从页面顶部算起。 - 保留代码里 `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、有赞这类建站平台装不进代码,告诉用户去平台自带的动效设置里找。 ## 参数 | 参数 | 默认值 | 作用 | | --- | --- | --- | | `box` | | 滚动盒子的 ref;整页滚动时不传 | ## 你可以说 > 首屏做成 Parallax 视差滚动:往下滚时,天空和远山动得慢,近处的山和文字动得快,看起来有纵深。 ## 完整代码 ```tsx import {useEffect, useRef, useState, type RefObject} from 'react'; /* --------------------------------------------------------------- * Parallax 视差滚动 * 往下滚的时候,远处的层(天空、远山)比近处的层(前山、文字)动得慢,看起来有纵深。 * useScrollY:读滚动距离。用在整页上时不传参数;放在可滚动的盒子里时,传这个盒子。 * 每一层用 translateY(滚动距离 × 速度) 移动,速度越小越"远"。 * ------------------------------------------------------------- */ export function useScrollY(box?: RefObject) { const [y, setY] = useState(0); useEffect(() => { const el = box?.current; let raf = 0; const read = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(() => setY(el ? el.scrollTop : window.scrollY)); }; const target = el ?? window; target.addEventListener('scroll', read, {passive: true}); return () => { target.removeEventListener('scroll', read); cancelAnimationFrame(raf); }; }, [box]); return y; } /* ---------------- 示例:「山野」官网首屏 ---------------- */ export default function ParallaxDemo() { const box = useRef(null); const raw = useScrollY(box); const reduce = typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches; const y = reduce ? 0 : raw; const layer = (k: number) => ({transform: `translate3d(0, ${y * k}px, 0)`, willChange: 'transform'}); return (
60 ? 'rgba(18,19,47,.82)' : 'transparent', backdropFilter: raw > 60 ? 'blur(12px)' : 'none'}} > 山野 {['路线', '营地', '装备'].map((n) => {n})}
{Array.from({length: 30}, (_, i) => )} {Array.from({length: 22}, (_, i) => { const x = i * 42 + (i * 7) % 18, h = 50 + (i * 23) % 60; return ; })}

探索山野

周末,去山里走走

开始探索

热门路线

{[['/tex/s2.jpg', '四姑娘山 · 2 日'], ['/tex/s6.jpg', '武功山 · 1 日'], ['/tex/s5.jpg', '莫干山 · 2 日']].map(([src, t]) => (
{t}
))}
); } ```