Framer Motion:如何实现初始动画仅执行一次后持续悬浮的效果?
解决Framer Motion元素入场后悬浮不返回的问题
你遇到的问题是因为默认的keyframes动画会循环或回到初始状态,以下是几种可行的方案,都是Framer Motion中实现一次性入场后保持状态的标准写法:
方案一:使用initial + animate属性(推荐)
这是Framer Motion实现入场动画最简洁的方式,动画结束后会自动保留最终状态,无需额外配置。
import { motion } from "framer-motion"; export default function FloatingElement() { return ( <motion.div // 初始状态:元素位于页面底部(完全超出视口) initial={{ y: "100vh" }} // 目标状态:垂直居中(根据元素高度调整偏移量,这里假设元素高100px) animate={{ y: "calc(50vh - 50px)" }} // 动画参数控制 transition={{ duration: 1.2, // 动画时长 ease: "easeOut", // 缓动效果,结束阶段减速更自然 }} style={{ width: "100px", height: "100px", backgroundColor: "#2196F3", position: "absolute", left: "50%", transform: "translateX(-50%)", // 水平居中 }} /> ); }
原理说明
initial定义组件挂载时的初始样式,animate定义目标样式,Framer Motion会自动生成从初始到目标的过渡动画,且动画完成后会将元素样式固定在animate的状态上,不会回退。
方案二:使用keyframes并禁用重复
如果你坚持要用keyframes实现,需要明确设置禁止重复动画,并强制保持最后一帧状态:
import { motion } from "framer-motion"; export default function FloatingElement() { return ( <motion.div // 定义keyframes动画序列 animate={[ { y: "100vh" }, // 起始帧:底部 { y: "calc(50vh - 50px)" } // 结束帧:居中 ]} // 关键配置:禁止重复 + 保持最后一帧 transition={{ duration: 1.2, repeat: 0, // 关闭动画重复 ease: "easeOut", fill: "forwards" // 强制保留动画最后一帧的样式 }} style={{ width: "100px", height: "100px", backgroundColor: "#2196F3", position: "absolute", left: "50%", transform: "translateX(-50%)", }} /> ); }
方案三:手动控制动画触发(useAnimation钩子)
如果需要手动触发动画(比如点击按钮后执行),可以使用useAnimation钩子:
import { motion, useAnimation } from "framer-motion"; import { useEffect } from "react"; export default function FloatingElement() { const controls = useAnimation(); // 组件挂载后触发动画 useEffect(() => { controls.start({ y: "calc(50vh - 50px)", transition: { duration: 1.2, ease: "easeOut" } }); }, [controls]); return ( <motion.div initial={{ y: "100vh" }} animate={controls} style={{ width: "100px", height: "100px", backgroundColor: "#2196F3", position: "absolute", left: "50%", transform: "translateX(-50%)", }} /> ); }
扩展用法
你可以把controls.start()绑定到按钮点击事件,实现按需触发动画:
<button onClick={() => controls.start({ y: "calc(50vh - 50px)" })}> 启动动画 </button>
内容的提问来源于stack exchange,提问作者DemianH
相关产品推荐
相关产品推荐

