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

使用mouseenter事件切换状态时CSS transition属性失效问题

鼠标悬停按钮时文本过渡动画失效的解决方案

问题场景

想实现鼠标悬停按钮时,按钮旁显示带过渡动画的文本。使用按钮的click事件时动画正常,但换成mouseenter和mouseleave事件后,CSS transition动画完全不生效。

用到的CSS过渡代码:

#text-container {
  transition: max-width 3s ease;
  overflow-x: hidden;
}

对应的React代码:

import { useEffect, useState } from 'react';
import './App.css';

const TextComponent = () => {
  const [maxWidth, setMaxWidth] = useState('0vw');

  useEffect(() => {
    setMaxWidth('100vw');
  }, []);

  return (
    <div id="text-container" style={{ maxWidth, paddingLeft: '8px' }}>
      <div style={{ textWrap: 'nowrap' }}>Sample text??</div>
    </div>
  );
};

function App() {
  const [show, setShow] = useState(false);

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'flex-start',
        width: '300px',
      }}
    >
      <button
        onClick={() => setShow((prevShow) => !prevShow)}
        // onMouseEnter={() => setShow(true)}
        // onMouseLeave={() => setShow(false)}
      >
        Show Text
      </button>

      {show ? <TextComponent /> : null}
    </div>
  );
}

export default App;

问题原因

核心问题在于组件挂载时机和浏览器样式优化:

  • 触发mouseenter时,TextComponent才开始挂载,组件渲染时初始maxWidth: 0vw的状态刚被设置,useEffect就立刻把它改成100vw。
  • 浏览器会合并这两次连续的样式修改,直接渲染最终的100vw状态,不会触发过渡动画。
  • 而click事件第一次点击时逻辑和hover一样,但第二次点击是切换状态(组件从显示到隐藏再到显示),hover则是每次进入都重新挂载组件,没有给浏览器留下捕捉初始样式的时间。

解决方案

方案1:始终渲染组件,用状态控制样式(推荐)

不要通过条件渲染销毁/挂载组件,而是让组件一直存在,直接用show状态控制maxWidth的值,这样状态变化时浏览器能捕捉到样式的前后变化,触发过渡:

修改后的App.tsx:

import { useState } from 'react';
import './App.css';

function App() {
  const [show, setShow] = useState(false);

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'flex-start',
        width: '300px',
      }}
    >
      <button
        onMouseEnter={() => setShow(true)}
        onMouseLeave={() => setShow(false)}
      >
        Show Text
      </button>

      <div 
        id="text-container" 
        style={{ 
          maxWidth: show ? '100vw' : '0vw', 
          paddingLeft: '8px',
          overflowX: 'hidden',
          transition: 'max-width 3s ease'
        }}
      >
        <div style={{ textWrap: 'nowrap' }}>Sample text??</div>
      </div>
    </div>
  );
}

export default App;

方案2:延迟修改样式,让浏览器捕捉初始状态

如果一定要保留组件挂载的逻辑,可以在useEffect中加一个微任务延迟,让浏览器先渲染初始的0vw状态,再修改为100vw:

修改TextComponent:

const TextComponent = () => {
  const [maxWidth, setMaxWidth] = useState('0vw');

  useEffect(() => {
    // 用setTimeout让浏览器先完成初始渲染
    const timer = setTimeout(() => {
      setMaxWidth('100vw');
    }, 0);
    // 组件卸载时清除定时器
    return () => clearTimeout(timer);
  }, []);

  return (
    <div id="text-container" style={{ maxWidth, paddingLeft: '8px' }}>
      <div style={{ textWrap: 'nowrap' }}>Sample text??</div>
    </div>
  );
};

同时处理鼠标离开时,需要先触发反向动画,再销毁组件:

function App() {
  const [show, setShow] = useState(false);
  const [isExpanded, setIsExpanded] = useState(false);

  const handleMouseEnter = () => {
    setShow(true);
    // 延迟触发展开动画
    setTimeout(() => setIsExpanded(true), 0);
  };

  const handleMouseLeave = () => {
    setIsExpanded(false);
    // 等待动画结束后再隐藏组件
    setTimeout(() => setShow(false), 3000); // 时长和transition保持一致
  };

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'flex-start',
        width: '300px',
      }}
    >
      <button
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
      >
        Show Text
      </button>

      {show && (
        <div 
          id="text-container" 
          style={{ 
            maxWidth: isExpanded ? '100vw' : '0vw', 
            paddingLeft: '8px',
            overflowX: 'hidden',
            transition: 'max-width 3s ease'
          }}
        >
          <div style={{ textWrap: 'nowrap' }}>Sample text??</div>
        </div>
      )}
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 15:58:18