使用Framer Motion实现滚动进度条效果失败,求排查问题
滚动进度条不生效的问题排查与修复
问题描述
想要实现滚动时进度条宽度随滚动进度增加的效果,使用Framer Motion的useScroll和useSpring Hook但未成功。原代码如下:
组件代码
import { Grid, Stack, Typography, styled } from "@mui/material"; import { motion, useScroll, useSpring, useTransform } from "framer-motion"; import { useEffect, useRef } from "react"; const ProgressLineStyled = styled("div")(({ theme }) => ({ height: "10px", borderRadius: "10px", backgroundColor: "white", })); function Projects() { const ref = useRef(null); const { scrollYProgress } = useScroll({ container: ref, offset: ["end end", "start start"], }); const scalesX = useSpring(scrollYProgress, { damping: 30, stiffness: 100 }); return ( <Grid sx={{ pt: 12 }} height="100%" position="relative" component={motion.div} border="12px solid" container > <Grid component={motion.div} display="flex" flexDirection="column" item xs={11} ref={ref} > <Stack component={motion.div} direction="column" justifyContent="center" alignItems="center" position="sticky" top="50px" left="0%" gap={2} > <Typography component={motion.div} color="orange" variant="h4"> Projects </Typography> <ProgressLineStyled component={motion.div} style={{ scaleX: scalesX }} ></ProgressLineStyled> </Stack> {Array(5) .fill(null) .map((e, i) => { return ( <section className="section"> <span>work {i}</span> </section> ); })} </Grid> </Grid> ); } export default Projects;
section标签样式
.section{ height:100vh; scroll-snap-align: center; }
错误原因分析
- 滚动容器未配置可滚动属性:绑定
ref的Grid容器没有设置固定高度和overflow-y: auto,导致浏览器无法识别它为滚动容器,useScroll无法捕获滚动事件。 - scaleX变换原点错误:默认情况下
scaleX的变换原点是元素中心,进度条会从中间向两侧扩展,不符合"从左到右填充"的预期效果。 - useScroll的offset参数逻辑反了:
["end end", "start start"]表示当容器底部与视口底部对齐时进度为0,容器顶部与视口顶部对齐时进度为1,和"滚动向下时进度增加"的需求刚好相反。
修复后的代码
组件代码
import { Grid, Stack, Typography, styled } from "@mui/material"; import { motion, useScroll, useSpring } from "framer-motion"; import { useRef } from "react"; const ProgressLineStyled = styled("div")(({ theme }) => ({ height: "10px", borderRadius: "10px", backgroundColor: "white", // 设置变换原点为左侧 transformOrigin: "left center", })); function Projects() { const ref = useRef(null); const { scrollYProgress } = useScroll({ container: ref, // 修正offset:滚动开始时进度0,滚动结束时进度1 offset: ["start start", "end end"], }); const scalesX = useSpring(scrollYProgress, { damping: 30, stiffness: 100 }); return ( <Grid sx={{ pt: 12, height: "100vh" }} // 给外层容器设置全屏高度 position="relative" component={motion.div} border="12px solid" container > <Grid component={motion.div} display="flex" flexDirection="column" item xs={11} ref={ref} sx={{ height: "100%", // 给滚动容器设置固定高度 overflowY: "auto", // 开启垂直滚动 }} > <Stack component={motion.div} direction="column" justifyContent="center" alignItems="center" position="sticky" top="50px" left="0%" gap={2} sx={{ zIndex: 1 }} // 确保进度条在内容上方 > <Typography component={motion.div} color="orange" variant="h4"> Projects </Typography> <ProgressLineStyled component={motion.div} style={{ scaleX: scalesX }} ></ProgressLineStyled> </Stack> {Array(5) .fill(null) .map((e, i) => { return ( <section className="section" key={i}> {/* 添加key避免警告 */} <span>work {i}</span> </section> ); })} </Grid> </Grid> ); } export default Projects;
保持原section样式不变
.section{ height:100vh; scroll-snap-align: center; }
关键修改点说明
- 给外层Grid设置
height: "100vh",给滚动容器(绑定ref的Grid)设置height: "100%"和overflowY: "auto",确保它成为可滚动容器。 - 在
ProgressLineStyled中添加transformOrigin: "left center",让进度条从左侧开始缩放。 - 将
useScroll的offset改为["start start", "end end"],使滚动从顶部到底部时进度从0到1递增。 - 给循环渲染的section添加
key属性,消除React警告。
内容的提问来源于stack exchange,提问作者Abdullah Asif
相关产品推荐
相关产品推荐

