移动端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)配合状态控制,这是更符合浏览器动画机制的流畅方案:
- 给Autocomplete的下拉菜单(Paper组件)添加自定义过渡样式:
const paperStyle = { transition: isMobile ? 'transform 0.3s ease' : 'none', transform: isMobile ? `translateX(${acPaperTransX}px)` : 'none' };
- 在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
相关产品推荐
相关产品推荐

