
本文详解如何使用 Framer Motion 的 useScroll 与 useTransform 实现元素在视口中心时完全不透明、向上或向下滚动时平滑淡出的双向滚动动画,关键在于正确配置 offset 参数。
本文详解如何使用 framer motion 的 `usescroll` 与 `usetransform` 实现元素在视口中心时完全不透明、向上或向下滚动时平滑淡出的双向滚动动画,关键在于正确配置 `offset` 参数。
在 Framer Motion 中,要让一个元素(如标题文字)随滚动位置动态控制其透明度——在用户滚动至该元素垂直居中于视口时显示最清晰(opacity = 1),而向上或向下偏离中心时逐渐淡出(opacity → 0)——核心挑战不在于动画逻辑本身,而在于对 useScroll 的 offset 配置的理解与精准设定。
你当前的代码:
const ref = useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "end end"], // ❌ 问题根源在此
});
const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [0, 1, 0]);使用 ["start end", "end end"] 意味着:
-
scrollYProgress = 0:当目标元素顶部与视口底部对齐时; -
scrollYProgress = 1:当目标元素底部与视口底部对齐时。
这定义的是一个单向滚动区间(从元素刚进入视口底部 → 完全离开视口底部),因此 scrollYProgress 仅在元素经过该窄带时线性变化,无法反映“以中心为锚点”的对称行为,导致动画只在向下滚动时响应,向上滚动无变化。
✅ 正确解法:将滚动锚点设为元素的 center(中心),并选择对称的触发边界:
const ref = useRef<HTMLDivElement>(null);
// ✅ 关键修正:用 ["start center", "end center"]
// 表示:scrollYProgress = 0 → 元素顶部与视口中心对齐
// scrollYProgress = 1 → 元素底部与视口中心对齐
// 这样,当元素中心恰好位于视口中心时,scrollYProgress = 0.5
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start center", "end center"],
});
// 将 [0, 0.5, 1] 映射为 [0, 1, 0],实现中心最亮、两端渐隐
const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [0, 1, 0]);
// 可选:添加轻微缓动提升自然感(非必需,但推荐)
const opacitySmooth = useTransform(opacity, [0, 1], [0, 1], {
ease: "circOut",
});完整组件示例:
import { useRef } from "react";
import { motion, useScroll, useTransform } from "framer-motion";
export default function ScrollFadeSection() {
const ref = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start center", "end center"],
});
const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [0, 1, 0]);
return (
<div
ref={ref}
className="flex flex-col items-center justify-center min-h-screen border border-blue-600 text-7xl bg-gradient-to-b from-gray-50 to-gray-100"
>
<motion.div
style={{ opacity }}
className="font-bold tracking-tight"
>
Hello
</motion.div>
</div>
);
}? 为什么 ["start center", "end center"] 是最优解?
- 它将滚动进度
scrollYProgress的 0→1 区间,严格绑定在「元素顶部到达视口中心」到「元素底部到达视口中心」这一段——而这恰恰是以元素中心为对称轴的 100% 覆盖范围; - 当
scrollYProgress = 0.5时,元素中心必然与视口中心重合,完美满足你的设计意图; -
useTransform的三段映射[0, 0.5, 1] → [0, 1, 0]则天然构建出抛物线式衰减曲线,视觉上柔和、专业。
⚠️ 注意事项:
- 确保目标元素有明确高度(如
min-h-screen或固定height),否则offset计算可能失准; - 若需更精细控制(如提前/延后触发),可微调
offset为["start 80%", "end 20%"]等 CSS-in-js 偏移语法; - 在移动端或缩放页面中,建议结合
useReducedMotion()做无障碍降级:const shouldReduceMotion = useReducedMotion(); const finalOpacity = shouldReduceMotion ? 1 : opacity;
掌握 offset 的语义化表达(start/center/end + 容器位置),是解锁 Framer Motion 滚动动画精准控制的钥匙。无需复杂计算,一行配置即可实现专业级视差感知动画。


















