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

移动端MUI Autocomplete搜索栏平滑动画实现问题求助

MUI Autocomplete 移动端平滑动画修复方案

我需要给MUI Autocomplete搜索栏实现一个仅在移动端(屏幕宽度<600px)生效的平滑动画,但当前实现存在两个问题:

  • 下拉菜单关闭时定时器(interval)未正确重置:
    • useEffect依赖的是动态变化的searchActive;
    • 尝试用useState的回调函数,但组件未销毁,不确定该方式是否合理。
  • 通过setInterval修改下拉菜单宽度的过渡效果极不流畅。

当前核心组件逻辑

export function PrimarySearchAppBar() {
  const [searchActive, setSearchActive] = React.useState(null);
  const [acPaperWidth, setAcPaperWidth] = React.useState(null);
  const [acPaperTransX, setAcPaperTransX] = React.useState(0);

  const AcRef = React.useRef(null);
  const isMobile = useMediaQuery(useTheme().breakpoints.down('sm'));

  const options = top100Films.map((option) => {
    const group = option.group.toUpperCase();
    return {
      firstLetter: group,
      ...option,
    };
  });

  React.useLayoutEffect(() => {
    if (AcRef.current) {
      setAcPaperWidth(AcRef.current.offsetWidth);
    }
    console.log(acPaperWidth);
  }, [AcRef]);

  let interval;
  React.useEffect(() => {
    if (searchActive) {
      if (acPaperTransX <= 39) {
        interval = setInterval(() => {
          setAcPaperWidth(AcRef.current.offsetWidth);
          setAcPaperWidth((acPaperTransX) => acPaperTransX + 1);
          if (acPaperTransX >= 38) {
            clearInterval(interval);
          }
          console.log(acPaperTransX);
        }, 10);
      }
    } else {
      setAcPaperTransX(0);
      clearInterval(interval);
    }
  }, [searchActive]);

  return (
    <>Hello World</>
  );
}

问题1:定时器未正确重置的修复

  • 用useRef存储定时器实例,避免在effect外部声明导致的引用丢失问题;
  • 每次启动新定时器前清除旧实例,同时在组件卸载时自动清理定时器:
// 新增ref存储定时器
const intervalRef = React.useRef(null);

React.useEffect(() => {
  if (searchActive && isMobile) { // 仅在移动端触发动画
    if (acPaperTransX <= 39) {
      // 启动前清除旧定时器
      if (intervalRef.current) clearInterval(intervalRef.current);
      intervalRef.current = setInterval(() => {
        setAcPaperTransX(prev => {
          const newVal = prev + 1;
          // 达到目标值后清除定时器
          if (newVal >= 39) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
          }
          return newVal;
        });
      }, 10);
    }
  } else {
    // 关闭搜索时重置状态并清除定时器
    setAcPaperTransX(0);
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
  }

  // 组件卸载时强制清理定时器
  return () => {
    if (intervalRef.current) clearInterval(intervalRef.current);
  };
}, [searchActive, acPaperTransX, isMobile]); // 添加必要依赖

问题2:过渡不流畅的修复

放弃用setInterval逐帧修改状态的方案,改用CSS过渡(transition)配合状态控制,这是更符合浏览器动画机制的流畅方案:

  1. 给Autocomplete的下拉菜单(Paper组件)添加自定义过渡样式:
const paperStyle = {
  transition: isMobile ? 'transform 0.3s ease' : 'none',
  transform: isMobile ? `translateX(${acPaperTransX}px)` : 'none'
};
  1. 在Autocomplete组件中通过PaperProps应用样式:
<Autocomplete
  PaperProps={{ style: paperStyle }}
  // 其他必要props
/>

完整优化后的核心逻辑

export function PrimarySearchAppBar() {
  const [searchActive, setSearchActive] = React.useState(false);
  const [acPaperTransX, setAcPaperTransX] = React.useState(0);

  const AcRef = React.useRef(null);
  const isMobile = useMediaQuery(useTheme().breakpoints.down('sm'));
  const intervalRef = React.useRef(null);

  const options = top100Films.map((option) => {
    const group = option.group.toUpperCase();
    return {
      firstLetter: group,
      ...option,
    };
  });

  React.useEffect(() => {
    if (searchActive && isMobile) {
      if (intervalRef.current) clearInterval(intervalRef.current);
      // 用16ms间隔接近屏幕刷新率,提升流畅度
      intervalRef.current = setInterval(() => {
        setAcPaperTransX(prev => {
          const newVal = prev + 2;
          if (newVal >= 40) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
          }
          return newVal;
        });
      }, 16);
    } else {
      setAcPaperTransX(0);
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
        intervalRef.current = null;
      }
    }

    return () => {
      if (intervalRef.current) clearInterval(intervalRef.current);
    };
  }, [searchActive, isMobile]);

  const paperStyle = {
    transition: isMobile ? 'transform 0.3s ease' : 'none',
    transform: isMobile ? `translateX(${acPaperTransX}px)` : 'none'
  };

  return (
    <Autocomplete
      ref={AcRef}
      options={options}
      PaperProps={{ style: paperStyle }}
      renderInput={(params) => (
        <TextField 
          {...params} 
          label="搜索" 
          onFocus={() => setSearchActive(true)}
          onBlur={() => setSearchActive(false)}
        />
      )}
    />
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:05:39