# 速度跑马灯 Velocity Marquee > 一行大字平时慢慢走,滚动越快跑得越快、文字跟着倾斜,往回滚就倒着跑。 分类:动效 · 角色:点缀(贴在某个元素上,一页两三个) [全部组件和流程](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。 - 常搭配:[视差滚动 Parallax](https://www.dachuiai.com/c/parallax.md)、[横向滚动 Horizontal Scroll](https://www.dachuiai.com/c/horizontal-scroll.md)、[物理掉落 Physics Drop](https://www.dachuiai.com/c/physics-drop.md) ## 装进项目 - 写法:React 18 以上 + TypeScript + Tailwind CSS v4,一个文件就能用,不引用别的组件。 - 依赖:只用 react,不用另外安装 - 文件:原样存成 `velocity-marquee.tsx`(比如 `src/components/velocity-marquee.tsx`)。能跑命令就下载[原文](https://www.dachuiai.com/code/velocity-marquee.tsx),比手抄准:`curl -fsSL -o src/components/velocity-marquee.tsx https://www.dachuiai.com/code/velocity-marquee.tsx`(Windows 写 `curl.exe`)。下载完看一眼第一行是 `import`,不是网页。 - 导出:`VelocityMarquee` 是可以直接用的部分;最后的 `export default` 是演示示例,只作参考。 - 保留代码里 `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、有赞这类建站平台装不进代码,告诉用户去平台自带的动效设置里找。 ## 参数 | 参数 | 默认值 | 作用 | | --- | --- | --- | | `text` | | 要显示的文字 | | `base` | `0.9` | 不滚动时自己走的速度 | | `reverse` | `false` | 反方向 | | `scroller` | | 滚动盒子的 ref;整页滚动时不传 | | `className` | `''` | 加在最外层的类名,用来调大小、位置、间距 | ## 你可以说 > 大标题做成 Velocity Marquee 速度跑马灯:平时慢慢走,滚动越快跑得越快,文字跟着倾斜,往回滚就倒着跑。 ## 完整代码 ```tsx import {useEffect, useRef, type RefObject} from 'react'; /* --------------------------------------------------------------- * VelocityMarquee 速度跑马灯 * 一行大字平时慢慢走;滚动越快它跑得越快,文字还会跟着歪; * 往回滚,它就倒着跑。整个页面像被你的手指带动着。 * 整页用时 scroller 不传;放在可滚动的盒子里时,传这个盒子。 * ------------------------------------------------------------- */ type Props = {text: string; base?: number; reverse?: boolean; scroller?: RefObject; className?: string}; export function VelocityMarquee({text, base = 0.9, reverse = false, scroller, className = ''}: Props) { const track = useRef(null); useEffect(() => { const el = scroller?.current, node = track.current!; const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches; if (reduce) return; const read = () => (el ? el.scrollTop : scrollY); let raf = 0, x = 0, last = read(), vel = 0, dir = reverse ? -1 : 1; const tick = () => { raf = requestAnimationFrame(tick); const y = read(), dy = y - last; last = y; vel += (dy - vel) * 0.12; // 平滑后的滚动速度 if (Math.abs(vel) > 0.4) dir = (vel > 0 ? 1 : -1) * (reverse ? -1 : 1); x -= dir * (base + Math.abs(vel) * 0.9); const w = node.scrollWidth / 2; x = ((x % w) - w) % w; // 内容复制了一份,走满一份就无缝接回去 const skew = Math.max(-16, Math.min(16, vel * 0.55)) * (reverse ? -1 : 1); node.style.transform = `translate3d(${x}px, 0, 0) skewX(${skew}deg)`; }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [base, reverse, scroller]); return (
{[0, 1].map((k) => {`${text} `.repeat(6)})}
); } /* ---------------- 示例:滚动中的大标题 ---------------- */ export default function VelocityMarqueeDemo() { const box = useRef(null); return (
); } ```