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

Framer Motion退出动画绕非中心轴旋转,元素偏移问题求助

问题解决思路及代码修复

问题根源

  1. 两个独立的AnimatePresence容器无法同步控制图标的进入/退出动画,导致切换时序错乱
  2. 旋转原点(origin)设置不一致且未固定,加上没有给容器固定尺寸,旋转时触发布局重排造成偏移
  3. exit动画参数设置错误,未实现"旋转消失"效果,反而让元素停留在原位引发布局冲突

修复后的代码

import { useAppDispatch, useAppSelector } from '@/redux/hooks';
import { updateShowMore } from '@/slices/navigation.slice';
import React from 'react';
import { BsJustify } from 'react-icons/bs';
import { MdOutlineClose } from 'react-icons/md';
import classes from './ViewIcon.module.css';
import { motion, AnimatePresence } from 'framer-motion';

export default function ViewIcon() {
    const { showMore } = useAppSelector((state) => state.navigation);
    const dispatch = useAppDispatch();
    const toggleResults = (value: boolean) => dispatch(updateShowMore(value));

    return (
        <div style={{ width: '24px', height: '24px', position: 'relative' }}>
            <AnimatePresence mode="wait">
                {showMore ? (
                    <motion.div
                        key="close-icon"
                        initial={{ rotateZ: -180, opacity: 0 }}
                        animate={{ rotateZ: 0, opacity: 1 }}
                        exit={{ rotateZ: 180, opacity: 0 }}
                        style={{ origin: 'center', position: 'absolute', inset: 0 }}
                    >
                        <MdOutlineClose
                            className={classes.ShowMore}
                            onClick={() => toggleResults(false)}
                        />
                    </motion.div>
                ) : (
                    <motion.div
                        key="menu-icon"
                        initial={{ rotateZ: 180, opacity: 0 }}
                        animate={{ rotateZ: 0, opacity: 1 }}
                        exit={{ rotateZ: -180, opacity: 0 }}
                        style={{ origin: 'center', position: 'absolute', inset: 0 }}
                    >
                        <BsJustify
                            className={classes.ShowMore}
                            onClick={() => toggleResults(true)}
                        />
                    </motion.div>
                )}
            </AnimatePresence>
        </div>
    );
}

关键改动说明

  • 合并AnimatePresence容器:将两个图标放在同一个容器中,通过mode="wait"控制动画时序(若需要同时切换可去掉该属性),确保动画同步
  • 固定容器尺寸+绝对定位:父容器设置固定宽高,图标用绝对定位铺满容器,避免切换时触发布局重排,彻底解决偏移问题
  • 统一旋转原点:设置origin: 'center',让图标围绕中心旋转,消除因原点偏移导致的位置变化
  • 调整动画参数:exit时添加旋转+透明度变化实现"旋转消失",进入动画对应反向旋转+淡入,达成无缝切换效果

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 03:05:37