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

组件挂载时无法触发useAnimate动画的问题求助

问题解决:Framer Motion useAnimate 挂载动画不生效

问题原因

你的代码未给组件设置初始缩放状态,animate 方法是基于元素当前样式过渡到目标样式。组件挂载时元素默认 scale 为1,此时执行 animate 到 scale:1 自然不会产生动画效果。另外,卸载回调里的样式设置无意义,因为组件即将被移除,不会触发视觉变化。

修复方案

给组件元素设置初始缩放值,再通过 useAnimate 触发从初始值到目标值的过渡动画。若后续需要退出动画,需配合 AnimatePresence 组件(先解决挂载动画问题)。

修改后的 StupidComponent.tsx:

import React, { useEffect } from 'react'
import { useAnimate } from 'framer-motion'

export function StupidComponent() {
  const [scope, animate] = useAnimate()

  useEffect(() => {
    // 从初始的scale:0.5动画到scale:1
    animate(
      scope.current,
      { scale: 1 },
      { duration: 0.5, ease: 'easeInOut' }
    )
  }, [animate, scope])

  return (
    <div 
      ref={scope} 
      className="p-4 bg-orange-400 text-white"
      // 设置初始缩放状态
      style={{ transform: 'scale(0.5)' }}
    >
      This component scales to 1 when it appears.
    </div>
  )
}

扩展:添加退出动画(可选)

若要实现卸载时的动画,不能直接用 {showStupid && <StupidComponent />},需用 AnimatePresence 包裹组件并添加唯一 key,同时在组件内处理退出逻辑:

修改 App.tsx:

import { useState } from 'react'
import { AnimatePresence } from 'framer-motion'
import { StupidComponent } from '@/components/StupidComponent'

export const App = () => {
  const [showStupid, setShowStupid] = useState(false)

  return (
    <>
      <button
        onClick={() => {
          setShowStupid((prev) => !prev)
        }}
      >
        Show stupid
      </button>
      {/* 用AnimatePresence包裹,开启退出动画支持 */}
      <AnimatePresence>
        {showStupid && (
          <StupidComponent key="stupid-component" />
        )}
      </AnimatePresence>
    </>
  )
}

修改后的 StupidComponent.tsx(支持退出动画):

import React, { useEffect, useRef } from 'react'
import { useAnimate } from 'framer-motion'

export function StupidComponent() {
  const [scope, animate] = useAnimate()
  const isMounted = useRef(false)

  useEffect(() => {
    isMounted.current = true
    // 挂载动画
    animate(scope.current, { scale: 1 }, { duration: 0.5, ease: 'easeInOut' })

    return () => {
      isMounted.current = false
      // 触发退出动画,等待完成后再卸载组件
      const animation = animate(scope.current, { scale: 0.5 }, { duration: 0.5, ease: 'easeInOut' })
      animation.finished.then(() => {
        if (!isMounted.current) {
          // 动画完成后组件已标记卸载,无需额外操作
        }
      })
    }
  }, [animate, scope])

  return (
    <div 
      ref={scope} 
      className="p-4 bg-orange-400 text-white"
      style={{ transform: 'scale(0.5)' }}
    >
      This component scales to 1 when it appears, and scales back when removed.
    </div>
  )
}

关键说明

  • 必须设置初始样式,确保动画有初始值与目标值的差异
  • 退出动画依赖 AnimatePresence,它会等待组件内动画完成后再执行卸载
  • 用 useRef 标记组件挂载状态,避免动画完成后出现内存泄漏问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 03:12:46