基于滚动位置用React和Framer Motion实现文字逐字变色
实现滚动驱动的逐字变色动画方案
基于React + Framer Motion,参考Polestar的滚动字符变色效果,核心思路是拆分字符独立控制 + 滚动进度映射动画触发时机,具体实现步骤如下:
1. 核心思路拆解
要实现逐字变色,必须把文本拆分为单个独立元素,再根据滚动位置,给每个字符分配不同的动画触发阈值,通过Framer Motion将滚动进度映射为字符的颜色变化。
2. 具体实现步骤
步骤1:拆分文本为单个字符组件
将目标文本拆分为数组,每个字符渲染成单独的motion.span,这样能单独控制每个字符的样式和动画状态。
步骤2:监听滚动进度
使用Framer Motion的useScroll钩子,监听目标区块的滚动位置,计算区块在视口中的滚动进度(0到1的范围)。通过offset参数定义进度的起止点:
["start end", "end start"]表示:当区块顶部刚进入视口底部时进度为0,当区块底部完全离开视口顶部时进度为1。
步骤3:映射滚动进度到字符颜色
对每个字符,根据其索引计算专属的触发进度(比如第n个字符的触发点为n / 总字符数),再用useTransform将滚动进度转化为字符的颜色值,实现滚动到对应位置时字符平滑变色。
3. 完整代码示例
import { motion, useScroll, useTransform } from "framer-motion"; import { useRef } from "react"; export default function ScrollTextColor() { const targetText = "PRECISION"; const sectionRef = useRef(null); // 监听目标区块的滚动进度 const { scrollYProgress } = useScroll({ target: sectionRef, offset: ["start end", "end start"] }); return ( <div className="App"> {/* 顶部占位区,用于触发滚动 */} <div style={{ height: "100vh", display: "flex", alignItems: "center", justifyContent: "center" }}> 向下滚动查看效果 </div> {/* 目标动画区块 */} <div ref={sectionRef} style={{ height: "100vh", display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "#f5f5f5" }} > <div style={{ fontSize: "8rem", fontWeight: "800", letterSpacing: "0.5rem" }}> {targetText.split("").map((char, index) => { // 计算当前字符的动画触发进度 const triggerPoint = index / targetText.length; // 映射滚动进度到颜色变化:从灰色过渡到黑色 const charColor = useTransform( scrollYProgress, [triggerPoint - 0.1, triggerPoint], ["#888888", "#000000"] ); return ( <motion.span key={index} style={{ color: charColor, transition: "color 0.3s ease" }} > {char} </motion.span> ); })} </div> </div> {/* 底部占位区 */} <div style={{ height: "100vh" }}></div> </div> ); }
4. 优化与扩展
- 动画过渡优化:调整
useTransform的输入范围(比如[triggerPoint - 0.1, triggerPoint])可以控制颜色过渡的快慢,数值区间越大过渡越平缓。 - 性能优化:如果文本过长,可将单个字符组件封装为
React.memo组件,避免不必要的重渲染。 - 扩展动画效果:除了颜色,还可以映射滚动进度到字符的缩放、字体粗细、透明度等属性,增强视觉层次。
内容的提问来源于stack exchange,提问作者Ayhan
相关产品推荐
相关产品推荐

