feat: 主题切换性能优化 — View Transitions API + 0.15s 统一过渡 + 设置页增强
【问题】
桌面端(Electron)切换主题时颜色变化速度不一致、明显卡顿。
根因分析(4 轮迭代):
1. 过渡时长不统一(body 0.3s / 组件 0.15s)→ 统一到 0.2s
2. antd CSS-in-JS 标签替换导致瞬间变色 → 尝试 cssVar(无效,仍走标签替换)
3. applyHtmlTheme 在 updater 回调中执行,与 antd CSS 变更不同帧 → 移入 useLayoutEffect
4. html * 全局过渡在 Electron 中为数千 DOM 节点同步插值 → 合成器掉帧
终极方案:View Transitions API(GPU 合成器 1 次 cross-fade 替代 N 个 CSS 过渡)
+ 精准 CSS 过渡覆盖 ~80 个 antd 容器类(回退方案)
【修改】
ThemeProvider.tsx — 核心重构
- applyHtmlTheme 从 setIsDark updater → useLayoutEffect([isDark])
确保自定义 CSS 变量与 antd CSS-in-JS 在同一次 React 提交中生效
- View Transitions API:document.startViewTransition + flushSync
GPU 合成器截取旧/新两帧做单次 cross-fade,消除多元素插值卡顿
- 浏览器不支持时自动回退到 CSS transition 方案
globals.css — 过渡策略
- body: 0.3s ease → 0.15s linear
- View Transitions 动画:::view-transition-old/new(root) 0.15s linear
- ~80 个 antd 容器类精准过渡(Layout/Card/Modal/Table/Input/
Select/Picker/Menu/Tabs/Tag/Alert/Empty/Result/Skeleton 等)
按功能分组:布局 → 表格 → 表单 → 导航 → 反馈
不覆盖交互组件自有 transition(Button/Switch/Slider 等)
linear 替代 ease — 匀速插值,桌面应用更利落
Layout.tsx / HomeContent.tsx / LeftPanel.tsx / ModelSelector.tsx
- 过渡参数对齐:全部 0.2s → 0.15s linear
SettingsPage.tsx — 用户体验
- Modal 取消 keyboard={false},Esc 键可关闭
- macOS:关闭按钮移至标题左侧(遵循 macOS HIG)
- afterClose 中 blur 当前焦点元素,防止聚焦跳到导航栏
【文档】
CHANGELOG.md — 新增 0.0.15 版本记录
CLAUDE.md — 新增主题系统架构章节(双轨颜色体系 / 时序 / 过渡策略)
This commit is contained in:
@@ -211,7 +211,7 @@ export function HomeContent() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transition: 'background 0.2s ease',
|
||||
transition: 'background 0.15s linear',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -283,7 +283,7 @@ export function HomeContent() {
|
||||
: hoveredDivider === 'left'
|
||||
? token.colorPrimary
|
||||
: token.colorBorder,
|
||||
transition: 'background 0.2s ease',
|
||||
transition: 'background 0.15s linear',
|
||||
}} />
|
||||
</div>
|
||||
</>
|
||||
@@ -311,7 +311,7 @@ export function HomeContent() {
|
||||
: hoveredDivider === 'right'
|
||||
? token.colorPrimary
|
||||
: token.colorBorder,
|
||||
transition: 'background 0.2s ease',
|
||||
transition: 'background 0.15s linear',
|
||||
}} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ export function LeftPanel() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transition: 'background 0.2s ease',
|
||||
transition: 'background 0.15s linear',
|
||||
zIndex: 10,
|
||||
}}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
@@ -166,7 +166,7 @@ export function LeftPanel() {
|
||||
: isHovered
|
||||
? token.colorPrimary
|
||||
: token.colorBorder,
|
||||
transition: 'background 0.2s ease',
|
||||
transition: 'background 0.15s linear',
|
||||
}} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ function ModelItem({ model, isSelected, onSelect }: ModelItemProps) {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 8,
|
||||
transition: 'background 0.15s ease, border-color 0.15s ease, opacity 0.15s ease',
|
||||
transition: 'background 0.15s linear, border-color 0.15s linear, opacity 0.15s linear',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
|
||||
@@ -5,14 +5,17 @@
|
||||
// - 通过 open/onClose props 控制显隐,不使用路由(避免主页面被卸载)
|
||||
// - Modal 居中弹出 + 遮罩层,首页内容始终保持挂载不动
|
||||
// - destroyOnHidden → 关闭即销毁 DOM,不堆内存
|
||||
// - 仅可通过标题栏 X 按钮关闭(mask 不可关闭、ESC 禁用)
|
||||
// - Esc 键可关闭(跨平台统一)
|
||||
// - macOS:关闭按钮移至标题左侧(遵循 macOS HIG)
|
||||
//
|
||||
// 对应 QT 原版:设置窗口为独立窗口叠加在主窗口之上,主窗口不销毁
|
||||
// ============================================================
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { Modal } from 'antd';
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { SettingOutlined, CloseOutlined } from '@ant-design/icons';
|
||||
import { useAppContext } from '@/contexts/app-context';
|
||||
import { isMacOS } from '@/utils/platform';
|
||||
import { ThemeSetting } from './blocks/ThemeSetting';
|
||||
import { StoragePathSetting } from './blocks/StoragePathSetting';
|
||||
import { SystemInfoSetting } from './blocks/SystemInfoSetting';
|
||||
@@ -26,18 +29,31 @@ interface SettingsPageProps {
|
||||
|
||||
export function SettingsPage({ open, onClose }: SettingsPageProps) {
|
||||
const { edition } = useAppContext();
|
||||
const isMac = isMacOS();
|
||||
|
||||
// 关闭后移除焦点,避免跑到导航栏设置按钮上
|
||||
const handleAfterClose = useCallback(() => {
|
||||
(document.activeElement as HTMLElement)?.blur();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
width={480}
|
||||
onCancel={onClose}
|
||||
afterClose={handleAfterClose}
|
||||
destroyOnHidden={true}
|
||||
mask={{ closable: false }}
|
||||
keyboard={false}
|
||||
closable={!isMac}
|
||||
footer={null}
|
||||
title={
|
||||
<span className="flex items-center gap-2">
|
||||
{isMac && (
|
||||
<CloseOutlined
|
||||
onClick={onClose}
|
||||
className="cursor-pointer text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
|
||||
/>
|
||||
)}
|
||||
<SettingOutlined />
|
||||
设置
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user