You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 05:32:47