博客视觉增强功能开发报告
摘要
一、项目背景
为 Flare Stack Blog(基于 React + TanStack Start + Cloudflare Workers 的 Fuwari 主题博客)新增视觉增强功能,要求:
- 半透明卡片:调节卡片背景透明度,保留主题色相
- 反重力粒子背景:Three.js 实现的洛伦兹吸引子粒子动画,跟随鼠标/手势
- 全屏背景图:支持亮/暗双模式背景图,与粒子分层叠加
- 后台配置:所有参数可通过站点设置面板调节,支持实时预览
- 亮暗适配:粒子颜色和背景图均支持亮/暗模式独立配置
二、技术架构
层级 | 技术选型 | 说明 |
|---|---|---|
数据库 | D1 (SQLite) + Drizzle ORM | 存储卡片透明度、粒子参数、背景图路径等配置 |
配置管理 |
| 统一配置解析与默认值合并 |
粒子引擎 | Three.js + @react-three/fiber | 基于洛伦兹吸引子的粒子系统 |
前端框架 | React 19 + TanStack Start | 服务端渲染 + 客户端交互 |
表单管理 | react-hook-form | 后台设置表单,嵌套对象脏检查 |
状态管理 | TanStack Query | 配置数据缓存与失效 |
样式方案 | Tailwind CSS + CSS 变量 | 动态主题色与透明度控制 |
三、核心难点与试错过程
3.1 嵌套对象脏检查失效(半透明卡片保存按钮不亮)
现象:后台设置中修改卡片透明度滑块,保存按钮不亮起isDirty 为 false)。
根因分析:
- 卡片透明度配置存储在嵌套对象 site.theme.default.glass 中
- react-hook-form 的 isDirty 对深层嵌套对象的变更检测不稳定
- settings 从数据库加载后直接 reset(settings),而数据库可能缺少 glass 字段,导致 defaultValues 中该路径不存在
- useController 的 defaultValue 会覆盖 reset 设置的值,使 dirty 状态无法触发
试错经历:
尝试次数 | 方案 | 结果 |
|---|---|---|
1 | 使用 | 字段未注册 |
2 | 使用 |
|
3 | 移除 | 字段未注册,仍不触发 dirty |
4 | 手动 | 成功检测变化,按钮亮起 |
最终方案:
关键洞察:react-hook-form 的 isDirty 对于深层嵌套对象不可靠,手动深度比较是最稳定的方案。问题的根源不在视图层,而在表单状态管理层的默认值同步机制。
3.2 后台预览与前台全屏的层级冲突
现象:粒子背景在后台预览时无法正确显示在小容器中,或前台被内容遮挡。
根因分析:
粒子组件使用 fixed inset-0 全屏定位,无法适配后台预览的小容器
前台页面 z-index 层级混乱,粒子 z-[-1] 被背景遮挡,z-11 遮挡按钮
移动端卡片容器有 bg-(--fuwari-card-bg) 背景色遮挡粒子
最终方案:
层级设计(从下到上):
最底层:z-[-2] 全屏背景图
第二层:z-0 反重力粒子
第三层:z-10 页面内容容器
第四层:z-30 Banner 区域
最上层:z-50 导航栏
关键洞察:单一组件在不同场景下需要不同的定位策略。通过 isPreview 模式注入容器尺寸,实现"一处定义,多处适配"。
3.3 配置字段在不同层级丢失(darkColor 保存后消失)
现象:darkColor 在后台设置中修改并保存后,刷新页面恢复为默认值。
根因分析:
darkColor 在 site-config.schema.ts 的多个 Schema 中缺失
blog.config.ts 中 chaos 默认值缺少 darkColor 字段
config.service.ts 的 resolveSiteConfig 中 chaos 默认值缺少 darkColor
settings/index.tsx 的 fixedSettings 中 chaos 默认值缺少 darkColor
最终方案:在所有涉及 chaos 默认值的位置统一添加 darkColor 字段。
关键洞察:配置字段的完整性依赖数据流全程,Schema、默认配置、服务端解析、表单初始化四个层级必须同步更新。
3.4 粒子颜色固化问题
现象:无论色相设为多少,亮色模式都显示深蓝及褐色粒子,暗色模式都显示天蓝及白色粒子。
根因分析:
Antigravity 组件的 colors 和 darkColors 是硬编码数组
用户配置的 color 只作为单一颜色传入,未生成同色系渐变
最终方案:根据用户选择的主色自动生成渐变色数组。
关键洞察:用户配置的是"主色",而非完整的颜色列表。通过算法生成同色系渐变,既保留了用户意图,又丰富了视觉效果。
3.5 移动端卡片遮挡粒子
现象:移动端主页滑到文章列表时,粒子不可见。
根因分析:
HomePage 组件的外层容器有 bg-(--fuwari-card-bg) 背景色
桌面端通过 md:bg-transparent 透明,但移动端保留背景色遮挡粒子
最终方案:移除移动端的卡片背景色。
四、技术实现亮点
4.1 CSS 变量驱动的主题色系统
4.2 洛伦兹吸引子粒子系统
基于混沌理论的粒子运动,轨迹不可预测但有序
鼠标/手势位置作为吸引子中心,产生交互感
30 FPS 帧率限制 + 粒子数量自适应,保证移动端流畅
4.3 双模式背景图系统
半屏模式:homeBg / darkHomeBg
全屏模式:fullscreenBg.light / fullscreenBg.dark
层级:背景图 → 粒子 → 卡片内容
4.4 配置合并与默认值补全
五、最终成果
卡片透明度:滑块控制(0.3~1),保留主题色相,亮暗模式独立生效
反重力粒子:洛伦兹吸引子动画,30 FPS 流畅运行,鼠标/手势交互
粒子配置:数量、速度、亮暗颜色、大小、聚集半径、磁力范围,全部可调
全屏背景:开关控制,亮/暗双模式背景图上传,与粒子分层叠加
后台预览:所有设置项带实时预览,粒子和卡片效果即调即见
移动端适配:卡片透明、粒子数量自适应、间距优化
数据持久化:所有配置正确保存,刷新不丢失
六、经验总结
表单脏检查在嵌套对象上不可靠:react-hook-form 的 isDirty 对深层嵌套对象支持有限,手动深度比较是最稳定的方案。
单一组件多场景定位:通过 isPreview 模式 + 容器尺寸监听,实现组件在不同容器中的自适应渲染。
配置字段完整性依赖数据流全程:Schema、默认配置、服务端解析、表单初始化四个层级必须同步更新,缺一不可。
用户配置的是"主色",不是完整色板:通过算法生成同色系渐变,既保留用户意图,又丰富视觉效果。
CSS 变量是主题切换的最佳方案:单一变量驱动所有样式,切换时无需重新渲染组件。
层级设计决定交互体验:清晰的分层是良好体验的基础。
试错是理解系统边界的必经之路:每一次失败都在揭示框架或架构的边界。
注:反粒子组件来自Besty0728,以下为仓库链接。
