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

为何我的Framer Motion徽标动画会执行两次?

解决Framer Motion徽标悬停动画移出时中间路径重复旋转的问题

问题分析

当前代码通过外部hover props控制动画状态,存在两个核心问题:

  1. 中间路径的middle变体中,rest状态未明确指定rotate: 0,鼠标移出时,Framer Motion会自动补间从hover后的旋转值(-3600度)过渡到0,视觉上表现为再次旋转。
  2. 使用animate配合外部状态管理,不如Framer Motion原生的whileHover适配悬停场景,容易出现状态切换时的动画异常。

解决方案

  1. 明确rest状态的旋转值:给middle的rest状态添加rotate: 0,确保初始和移出后的状态一致。
  2. 改用whileHover替代手动状态控制:直接使用Framer Motion的whileHover属性,自动处理悬停进入/退出的动画逻辑,无需手动传递hover props。
  3. 调整动画过渡逻辑:为非中间路径的动画添加once: true,确保悬停时只播放一次往返动画;中间路径的动画保持延迟触发,退出时直接恢复初始状态。

修改后的完整代码

import { motion, Variants } from "framer-motion";

interface LogoProps {
  width?: string;
  height?: string;
}

interface CustomVariants {
  [key: string]: Variants;
}

const Logo: React.FC<LogoProps> = (props: LogoProps) => {
  const customVariants: CustomVariants = {
    left: {
      rest: {
        x: 0
      },
      hover: {
        x: -20,
        transition: {
          repeat: 1,
          repeatType: "reverse",
          repeatDelay: 1,
          once: true
        }
      }
    },
    right: {
      rest: {
        x: 0
      },
      hover: {
        x: 20,
        transition: {
          repeat: 1,
          repeatType: "reverse",
          repeatDelay: 1,
          once: true
        }
      }
    },
    bottom: {
      rest: {
        y: 0
      },
      hover: {
        y: 20,
        transition: {
          repeat: 1,
          repeatType: "reverse",
          repeatDelay: 1,
          once: true
        }
      }
    },
    middle: {
      rest: {
        rotate: 0
      },
      hover: {
        rotate: -360 * 10,
        transition: {
          delay: 0.5,
          once: true
        }
      }
    }
  };

  return (
    <motion.svg
      width={props.width ?? 60}
      height={props.height ?? 60}
      viewBox="0 0 158 146"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      className="verifax-logo"
    >
      <motion.path
        id="side"
        d="M14.5017 66.3155C14.5327 75.092 14.8561 82.8129 15.1938 83.3718C15.4639 83.819 22.2984 87.9471 30.187 92.5369L44.6399 100.808L44.6607 85.4447C44.6485 76.9559 44.8577 69.0902 45.0151 67.811C45.3187 66.004 48.3905 64.16 60.19 58.5434C69.5292 53.9863 74.6938 50.9875 74.2886 50.3168C73.8834 49.6461 67.2327 45.5656 59.3629 41.2634L45.1125 33.3279L29.765 41.7966L14.4174 50.2652L14.5017 66.3155Z"
        fill="#6200EA"
        initial="rest"
        whileHover="hover"
        variants={customVariants.left}
      />
      <motion.path
        className="side"
        d="M61.1382 25.1267C61.3405 25.4616 67.2779 28.8833 74.2679 32.7662C81.4415 36.6965 87.9483 40.5483 88.8361 41.2492C89.724 41.95 90.5114 49.4043 90.4749 58.0574C90.371 66.5989 90.6078 73.9104 90.8101 74.2453C91.0124 74.5802 97.8331 71.2631 106.077 66.7141L120.823 58.5772L120.855 42.4844C120.757 33.6081 120.501 26.0092 120.164 25.451C119.894 25.0045 113.136 20.9935 105.142 16.4739L90.593 8.27822L75.7307 16.4792C67.6029 20.9641 61.0033 24.9034 61.1382 25.1267Z"
        fill="#6200EA"
        initial="rest"
        whileHover="hover"
        variants={customVariants.right}
      />
      <motion.path
        className="side"
        d="M59.6443 33.9735L59.5773 38.4802L70.0055 44.1985L80.5013 50.0285L63.9667 59.5989L47.4321 69.1693L47.5159 80.5954C47.6605 100.331 47.1504 98.974 52.497 96.0239C56.9137 93.5868 57.0599 93.0592 56.9503 84.9252C56.7873 69.52 55.3063 69.8902 74.0554 80.4036L90.9782 89.6898L104.926 81.9938L118.873 74.2978L114.437 71.8288L110.069 69.4716L99.1732 75.0366C93.178 78.1957 88.1425 80.3782 88.0075 80.1547C87.805 79.8194 87.6091 71.2861 87.503 61.3622L87.2685 43.0167L74.2791 35.8808C67.0983 31.9465 60.8736 28.8255 60.4087 29.082C59.9438 29.3386 59.5428 31.4967 59.6443 33.9735Z"
        fill="#6200EA"
        initial="rest"
        whileHover="hover"
        variants={customVariants.middle}
      />
      <motion.path
        className="side"
        d="M59.2076 92.5587L59.4758 108.66L74.4152 116.963L89.4708 125.202L104.821 116.732L120.171 108.262L120.087 92.2088L120.07 76.2673L104.982 84.1454L89.8948 92.0235L75.2068 83.8795C67.0168 79.3053 60.1024 75.8161 59.6372 76.0728C59.2884 76.2653 59.0604 83.8447 59.2076 92.5587Z"
        fill="#6200EA"
        initial="rest"
        whileHover="hover"
        variants={customVariants.bottom}
      />
    </motion.svg>
  );
};

export default Logo;

关键修改点说明

  • 移除hover props:不再需要手动传递hover状态,whileHover会自动监听鼠标悬停事件。
  • 明确middle的rest状态:添加rotate:0,确保移出时直接恢复初始旋转角度,无过渡动画。
  • 添加once属性:让非中间路径的往返动画仅在首次悬停时播放,避免重复触发。
  • 调整initial状态:将左侧路径的initial="hover"改为initial="rest",确保初始显示为收缩状态。

内容的提问来源于stack exchange,提问作者Sean

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:00:48