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

抽屉缩放动画后Tooltip位置未更新的问题求助

抽屉缩放后Tooltip未跟随/隐藏的解决方案

问题描述

我有一个可通过点击按钮调整大小的Drawer(抽屉),按钮带有Tooltip提示框。点击按钮触发抽屉缩放动画后,Tooltip停留在原位置,不会随按钮新位置调整。期望Tooltip要么跟随按钮移动,要么直接隐藏,而不是停在屏幕中间。尝试过通过onClick事件隐藏Tooltip但无效,需要在动画完成后触发处理逻辑。

核心原因

MUI的Tooltip默认基于触发元素的初始位置渲染,抽屉动画过程中元素位置变化时,Tooltip不会自动重新计算位置。直接在onClick中隐藏会因为动画尚未完成,Tooltip的状态无法同步更新。

解决方法

推荐通过监听抽屉动画结束事件来控制Tooltip的显示状态,实现点击时隐藏、动画完成后恢复的效果,步骤如下:

1. 给Sidebar添加动画结束回调

修改Sidebar组件,为Drawer的Paper元素绑定onTransitionEnd事件,通知父组件动画完成:

// Sidebar.tsx
import {CSSObject, styled, Theme} from "@mui/material/styles";
import MuiDrawer from "@mui/material/Drawer";
import React from "react";

const drawerWidth = 260;

const openedMixin = (theme: Theme): CSSObject => ({
    width: drawerWidth,
    transition: theme.transitions.create('width', {
        easing: theme.transitions.easing.sharp,
        duration: theme.transitions.duration.enteringScreen,
    }),
    overflowX: 'hidden',
});

const closedMixin = (theme: Theme): CSSObject => ({
    transition: theme.transitions.create('width', {
        easing: theme.transitions.easing.sharp,
        duration: theme.transitions.duration.leavingScreen,
    }),
    overflowX: 'hidden',
    width: `calc(${theme.spacing(7)} + 1px)`,
    [theme.breakpoints.up('sm')]: {
        width: `calc(${theme.spacing(8)} + 1px)`,
    },
});

const Drawer = styled(MuiDrawer, {shouldForwardProp: (prop) => prop !== 'open'})(
    ({theme, open}) => ({
        width: drawerWidth,
        flexShrink: 0,
        whiteSpace: 'nowrap',
        boxSizing: 'border-box',
        ...(open && {
            ...openedMixin(theme),
            '& .MuiDrawer-paper': openedMixin(theme),
        }),
        ...(!open && {
            ...closedMixin(theme),
            '& .MuiDrawer-paper': closedMixin(theme),
        }),
    }),
);

type Props = {
    open: boolean,
    children: any,
    onTransitionEnd?: () => void
}
const Sidebar = ({open, children, onTransitionEnd}: Props) => {
    return (
        <Drawer 
            variant="permanent" 
            open={open} 
            PaperProps={{
                elevation: 2,
                onTransitionEnd: onTransitionEnd
            }}
        >
            {children}
        </Drawer>
    )
}

export default Sidebar;

2. 父组件管理Tooltip状态

在Sidebar的父组件中,添加状态控制Tooltip的显示,点击抽屉按钮时先隐藏Tooltip,动画结束后恢复:

// 父组件示例
import { useState } from 'react';
import Sidebar from './Sidebar';
import SidebarFooter from './SidebarFooter';

const ParentComponent = () => {
    const [drawerOpen, setDrawerOpen] = useState(true);
    const [tooltipVisible, setTooltipVisible] = useState(true);

    const toggleDrawer = () => {
        setDrawerOpen(!drawerOpen);
        setTooltipVisible(false);
    };

    const handleDrawerTransitionEnd = () => {
        setTooltipVisible(true);
    };

    return (
        <div>
            <Sidebar open={drawerOpen} onTransitionEnd={handleDrawerTransitionEnd}>
                {/* 侧边栏其他内容 */}
                <SidebarFooter 
                    open={drawerOpen} 
                    toggleDrawer={toggleDrawer} 
                    tooltipVisible={tooltipVisible} 
                />
            </Sidebar>
        </div>
    );
};

export default ParentComponent;

3. 更新CustomTooltip和SidebarFooter

修改CustomTooltip,通过open属性控制显示状态;SidebarFooter传入该状态:

// CustomTooltip.tsx
import {Tooltip} from "@mui/material";

type Props = {
    title: string,
    placement?: 'bottom-end' | 'bottom-start' | 'bottom' | 'left-end' | 'left-start' | 'left' | 'right-end' | 'right-start' | 'right' | 'top-end' | 'top-start' | 'top',
    open?: boolean,
    children: any
}
const CustomTooltip = ({title, children, placement = 'right', open = true}: Props) => {
    return (
        <Tooltip 
            arrow 
            title={title} 
            placement={placement}
            open={open}
        >
            {children}
        </Tooltip>
    )
}

export default CustomTooltip;
// SidebarFooter.tsx
import { IconButton } from '@mui/material';
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import CustomTooltip from './CustomTooltip';

type Props = {
    open: boolean,
    toggleDrawer: () => void,
    tooltipVisible: boolean
}

const SidebarFooter = ({open, toggleDrawer, tooltipVisible}: Props) => {
    return (
        <CustomTooltip 
            title={open ? '隐藏' : '展开'} 
            open={tooltipVisible}
        >
            <div className={iconStyle}>
                <IconButton onClick={toggleDrawer}>
                    {open ? <ChevronLeftIcon/> : <ChevronRightIcon/>}
                </IconButton>
            </div>
        </CustomTooltip>
    )
}

export default SidebarFooter;

可选方案:让Tooltip自动跟随

如果希望Tooltip跟随按钮移动,可以基于MUI的Popper组件自定义Tooltip的定位逻辑,但实现复杂度较高,上述隐藏/恢复方案更简单直接,符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 23:01:03