diff --git a/client/electron/src/App.tsx b/client/electron/src/App.tsx index 09d0149..8013fe1 100644 --- a/client/electron/src/App.tsx +++ b/client/electron/src/App.tsx @@ -56,11 +56,25 @@ export default function App() { useEffect(() => { const root = document.documentElement; root.classList.remove('view-switch'); - // 读一次布局,强制样式失效(否则 remove/add 在同一帧里会被合并,动画不重放) - void root.offsetWidth; - root.classList.add('view-switch'); + /* + * 两次 rAF:让「移除 → 加上」落在两帧里,动画就会重放。 + * + * ★ 2026-09-15 用户:「动画卡顿严重,且绝大部分场景还是没有流畅的动画」。 + * 原先这里写的是 `void root.offsetWidth` —— 强制同步重排。它在**每次**切视图时 + * 都逼浏览器把整个文档布局算一遍(DOM 大、又挂着玻璃面时这笔账很贵), + * 而这笔开销恰好落在动画开始的那一帧上。rAF 同样能让 remove/add 分属两帧, + * 但不需要付重排的钱。 + */ + let second = 0; + const first = requestAnimationFrame(() => { + second = requestAnimationFrame(() => root.classList.add('view-switch')); + }); const t = window.setTimeout(() => root.classList.remove('view-switch'), 400); - return () => window.clearTimeout(t); + return () => { + cancelAnimationFrame(first); + if (second) cancelAnimationFrame(second); + window.clearTimeout(t); + }; }, [viewMode, commTab, narrowPane, composing]); const fetchInbox = useMailStore(s => s.fetchInbox); diff --git a/client/electron/src/components/CalendarView.tsx b/client/electron/src/components/CalendarView.tsx index 0338811..641c676 100644 --- a/client/electron/src/components/CalendarView.tsx +++ b/client/electron/src/components/CalendarView.tsx @@ -747,7 +747,7 @@ function DayAgendaPane({ }) { const isToday = isSameDay(day, new Date()); return ( -