Framer Motion中SVG pathLength异常:pathLength=1未对应完整路径
解决Framer Motion中多路径SVG的pathLength动画失效问题
问题原因
你当前使用的SVG path 是多条子路径合并的(d 属性包含多个 M 起始命令),而Framer Motion的 pathLength 属性默认针对单一路径设计。这种情况下,pathLength 的值无法正确映射到所有子路径的总长度,导致设置 pathLength: 0.5 时仍显示完整路径。
解决方案
方案1:拆分多路径为独立的motion.path元素
把原合并的路径拆分成多个单独的 <motion.path>,让每个子路径都能独立应用 pathLength 动画:
const handleSvg = async () => { const svgDrawDuration = 3; const svgFadeInOutDuration = 0.5; const timeUntilVisible = 1.5; const repeatDelay = 1; // 批量给所有路径设置动画 const animatePaths = (props, options) => { document.querySelectorAll('.svg-path').forEach(path => { startSvg(path, props, options); }); }; animatePaths({ strokeOpacity: 1 }, { duration: svgFadeInOutDuration, delay: timeUntilVisible }); await animatePaths({ pathLength: 1 }, { duration: svgDrawDuration, ease: 'easeInOut', delay: timeUntilVisible }); animatePaths({ strokeOpacity: 0 }, { duration: svgFadeInOutDuration, delay: (svgDrawDuration - svgFadeInOutDuration) + repeatDelay }); await animatePaths({ pathLength: 0 }, { duration: svgDrawDuration, ease: "easeInOut", delay: repeatDelay }); setTimeout(() => handleSvg(), 500); } // 拆分后的SVG结构 <svg ref={scope} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="M10.5 6h9.75" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="M10.5 6a1.5 1.5 0 1 1-3 0" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="m3 6a1.5 1.5 0 1 0-3 0" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="M3.75 6H7.5" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="m13.5 18h9.75" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="M10.5 18a1.5 1.5 0 0 1-3 0" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="m7.5 18a1.5 1.5 0 0 0-3 0" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="M3.75 18H7.5" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="m22.5 12h3.75" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="M19.5 12a1.5 1.5 0 0 1-3 0" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="m16.5 12a1.5 1.5 0 0 0-3 0" /> <motion.path className="svg-path svgAnimation" initial={{ strokeOpacity: 0, pathLength: 0 }} strokeLinecap="round" strokeLinejoin="round" d="M3.75 12h9.75" /> </svg>
方案2:使用strokeDasharray + strokeDashoffset替代pathLength
对于多路径,传统描边动画方式更可靠,通过动画 strokeDashoffset 实现绘制效果:
import { useRef, useEffect } from 'react'; import { motion, useAnimation } from 'framer-motion'; const SvgAnimation = () => { const pathRef = useRef(null); const controls = useAnimation(); useEffect(() => { const path = pathRef.current; if (!path) return; // 获取路径总长度 const totalLength = path.getTotalLength(); const handleAnimation = async () => { const svgDrawDuration = 3; const svgFadeInOutDuration = 0.5; const timeUntilVisible = 1.5; const repeatDelay = 1; // 初始状态:完全隐藏描边 await controls.start({ strokeOpacity: 0, strokeDasharray: totalLength, strokeDashoffset: totalLength, }); // 显示描边并开始绘制 await controls.start({ strokeOpacity: 1, transition: { duration: svgFadeInOutDuration, delay: timeUntilVisible } }); await controls.start({ strokeDashoffset: 0, transition: { duration: svgDrawDuration, ease: 'easeInOut', delay: timeUntilVisible } }); // 隐藏描边并反向绘制 await controls.start({ strokeOpacity: 0, transition: { duration: svgFadeInOutDuration, delay: (svgDrawDuration - svgFadeInOutDuration) + repeatDelay } }); await controls.start({ strokeDashoffset: totalLength, transition: { duration: svgDrawDuration, ease: 'easeInOut', delay: repeatDelay } }); setTimeout(handleAnimation, 500); }; handleAnimation(); }, [controls]); return ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <motion.path ref={pathRef} className={styles.svgAnimation} strokeLinecap="round" strokeLinejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75" animate={controls} /> </svg> ); };
额外注意事项
- 确保
svgAnimationCSS类设置了stroke宽度(比如stroke-width: 2),否则描边可能不可见。 - 当前示例是纯线条动画,建议给路径添加
fill: none,不需要的话可以移除fillOpacity相关动画。
内容的提问来源于stack exchange,提问作者Randomguy660
相关产品推荐
相关产品推荐

