feat(island): 彻底照搬 WinIsland 架构重写灵动岛(固定包围盒窗口 + 窗口内弹簧形变)
背景:上一版仍沿用"每帧移动/缩放透明窗口"的做法,实机上会出现形变抖动与残影
(用户反馈"越改越烂、开合震动还在")。本次按 WinIsland 的真实做法重做架构。
主进程(packages/desktop-client/src/main/island.ts):
- 窗口一次创建为**固定包围盒**(最大形态 + ISLAND_SHADOW_PAD 阴影留白),
开合过程中不再调用 setBounds;只在"尺寸/位置设置变化"时重排一次窗口
- 新增 setInteractive():默认 setIgnoreMouseEvents(true,{forward:true}) 整块穿透,
由渲染层命中测试回调切换(照搬 WinIsland set_cursor_hittest),保证不吞桌面点击
- 移除逐帧几何代码(animateBounds/boundsFor/computeBounds/resolveAnchor 用于窗口定位的简化版保留)
- 空闲细缝不再受 Windows 36px 最小窗口高度限制(窗口固定,岛可真正缩到 6px 宽)
渲染层(IslandApp.vue + 新增 spring.ts):
- 岛是**单一元素**,宽/高/圆角由 WinIsland 弹簧逐帧逼近:
force=(target-value)*0.10*dt → velocity=(velocity+force)*0.68^dt → value+=velocity*dt
(dt 以帧计;换向保留 35% 动量、单帧位移 ≤ 剩余距离 20%),并加零过冲钳制
- 胶囊层与展开层常驻并交叉淡入(展开 progress²、胶囊 1-1.5×progress),形变全程无缩放回弹
- 形状层改为显式像素宽高 + preserveAspectRatio="none"(避免 viewBox 缩放导致形状错位/残留角)
- 修两个实机可见缺陷:① 展开层漏了 flex-direction: column(展开卡内容横排、元素散落);
② 隐藏且无细缝时不再把岛缩成细缝(否则下次弹出会"从细缝长大")
- 尺寸增量 ISLAND_SIZE_DELTA / 阴影留白移入 @classhelper/shared,主/渲染两端共用一份
验证(verify:desktop 53 → 54 项,全绿):
- 新增「开合过程中窗口全程恒定」(逐帧窗口签名恒为 476x282@482,-2,岛 386x230→…→268x44 单调收缩,
SVG 形状 bbox 与卡片框一致 ±2px)
- 新增「岛外鼠标穿透」(岛内 interactive=true / 岛外 false)
- 开合不震动改为按"岛"判定:窗口冻结 + 岛锚点 0.00px + 无过冲;截图按岛矩形裁剪并统计岛外红像素=0
- 其余能力(通知/作业/叫人/紧急、标为已读、上课隐藏、6 锚点、字号可调、设置立即生效、空闲细缝)全部保留
This commit is contained in:
1 parent
93447922dc
commit
7a4668cb1b
8 files changed
+954
-410
No files matched your search
@@ -20,7 +20,7 @@
|
||||
| 1 | pnpm monorepo 脚手架、TypeScript / ESLint / Prettier / 环境变量 | ✅ 已完成 |
|
||||
| 2 | 后端:Prisma schema + 迁移 + 种子数据 + JWT 认证 + RBAC + 模块化 REST API + Socket.IO | ✅ 已完成 |
|
||||
| 3 | Web 管理端:登录、主布局、仪表盘、班级/学生/课表/作业/通知/成绩页面、Axios 封装、实时提示 | ✅ 已完成 |
|
||||
| 4 | EXE 客户端:Electron 主进程/preload/渲染进程、登录、课表/作业/通知/成绩/设置、实时推送、IndexedDB 离线缓存 | ✅ 已完成(冒烟验证 53/53) |
|
||||
| 4 | EXE 客户端:Electron 主进程/preload/渲染进程、登录、课表/作业/通知/成绩/设置、实时推送、IndexedDB 离线缓存 | ✅ 已完成(冒烟验证 54/54) |
|
||||
| 5 | 三端联调脚本、打包命令、完整 README | ✅ 已完成(三套安装包 + 两套 UI 回归测试) |
|
||||
| 6 | 测试账号与种子数据说明 | ✅ 已完成(见下文) |
|
||||
| 7 | 生产化:服务端安装程序(内置 Node)、Web 端 PWA 可安装、Docker + Nginx 部署、生产加固与运维文档 | ✅ 已完成(见 [`docs/production.md`](docs/production.md)) |
|
||||
@@ -179,7 +179,7 @@ pnpm verify:desktop # Electron 客户端(含灵动岛动画/收回/已读
|
||||
后端脚本验证**实时推送时延、作业完成、成绩下发、权限隔离、上课时段紧急通知拦截、导入与失败回滚、班级账号代全班操作、老师端导入不越权**等 133 项,
|
||||
实测通知 37ms、作业 26ms、成绩 25ms 到达(要求 < 5 秒),紧急通知 409 拦截与二次确认后发布均通过;
|
||||
客户端脚本验证 preload 桥接、渲染进程、IndexedDB 读写、断网回退、**灵动岛四种状态切换**与联网集成
|
||||
(**班级账号登录** + 四类数据 + Socket.IO),实测 53/53 通过;退出后再启动一次校验单实例锁与文件锁已释放(离线复跑 47/47)。
|
||||
(**班级账号登录** + 四类数据 + Socket.IO),实测 54/54 通过;退出后再启动一次校验单实例锁与文件锁已释放(离线复跑 48/48)。
|
||||
|
||||
## 默认账号与种子数据
|
||||
|
||||
@@ -323,12 +323,14 @@ pnpm verify:desktop # Electron 客户端(含灵动岛动画/收回/已读
|
||||
> 顶/底 × 左/中/右 **6 个停靠位**、可选"空闲细缝"(WinIsland `hidden_width`)。
|
||||
> 逐项从 WinIsland 源码提取的设计 token 见 **[docs/winisland-design-tokens.md](docs/winisland-design-tokens.md)**。
|
||||
>
|
||||
> 与 WinIsland 的两点**有意差异**(都写进了实现注释):
|
||||
>
|
||||
> 1. WinIsland 用"固定大窗口 + 画布内形变",我们沿用"按状态缓动窗口尺寸"——
|
||||
> 在 Electron 里前者需要常驻的点击穿透大窗口(容易吞掉桌面点击),后者已被 53 项断言覆盖(含 0.00px 不震动);
|
||||
> 2. Windows 对透明窗口有约 **36px 的最小高度**(实测请求 32px 得到 36px),
|
||||
> 因此"空闲细缝"的高度以 36px 为下限(WinIsland 因为窗口恒定所以不受此限)。
|
||||
> **架构也照搬 WinIsland(同一套做法,而不是"仿个外观")**:
|
||||
> 窗口一次创建成**固定包围盒**(最大形态 + 阴影留白),开合过程中**一帧都不移动/缩放窗口**;
|
||||
> 岛的全部形变(宽/高/圆角/内容交叉淡入)都发生在窗口内的渲染层,由 WinIsland 的弹簧驱动:
|
||||
> `force=(target-value)*0.10*dt` → `velocity=(velocity+force)*0.68^dt` → `value+=velocity*dt`
|
||||
> (dt 以帧为单位;换向只保留 35% 动量、单帧位移不超过剩余距离 20%),并额外加**零过冲钳制**(越过目标立即吸附)。
|
||||
> 鼠标命中照搬 `set_cursor_hittest`:窗口默认 `setIgnoreMouseEvents(true, { forward: true })` 整块穿透,
|
||||
> 渲染层做命中测试后只在指针进入岛体时打开命中——**大窗口不会吞掉桌面点击**(有专项断言)。
|
||||
> 由于窗口固定,Windows 那条"透明窗口最小高度约 36px"的限制不再作用于岛:空闲细缝可以真正做到 6px 宽。
|
||||
|
||||
| 场景 | 灵动岛行为 |
|
||||
| -------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -426,9 +428,9 @@ pnpm verify:desktop # Electron 客户端(含灵动岛动画/收回/已读
|
||||
- 「预览效果」按钮会本地推一条通知,立刻看到当前尺寸/配色/动画。
|
||||
- 落盘位置:客户端 `config.json` 的 `island` 字段(旧配置没有该字段时自动补默认值,向后兼容)。
|
||||
|
||||
**形变动画(需求 4 抖动修复 + 无震动)**:窗口尺寸由**单条帧循环**驱动(优先 `requestAnimationFrame`,
|
||||
主进程没有该全局时退化为 ~60fps 定时器),新动画会取消上一条并作废令牌,结束时对齐最终尺寸;
|
||||
卡片始终是**固定尺寸 + 顶部居中锚定**,只让窗口露出/裁切,形变过程中卡片上边缘不动;
|
||||
**形变动画(需求 4 抖动修复 + 无震动)**:岛在**固定包围盒窗口内**由弹簧逐帧逼近目标尺寸/圆角
|
||||
(`src/island/spring.ts`,照搬 WinIsland 的 `physics.rs` 参数),窗口本身全程不动;
|
||||
胶囊层与展开层用 WinIsland 的几何进度交叉淡入(展开层 `progress²`、胶囊层 `1 - 1.5×progress`);
|
||||
展开动画结束后才取焦点。
|
||||
|
||||
为了让"开合"完全没有振动感,几何与缓动做了三件事(每一条都有实测断言):
|
||||
@@ -534,7 +536,7 @@ pnpm dist:win # nsis 安装包 + portable 单文件(需联网下载 NSIS
|
||||
| **退出后无残留(文件锁 + 二次启动)** | ✅ 配置目录可删除(句柄已释放)→ 用同一配置目录二次启动成功并再次全绿 |
|
||||
|
||||
该验证对**开发产物与打包后的 EXE 都适用**(打包后用 `ELECTRON_SMOKE_RESULT=<file>` 写出 JSON 结果,
|
||||
已实测 `packaged: true`、53/53 通过、退出码 0)。灵动岛各状态的窗口截图会写到
|
||||
已实测 `packaged: true`、54/54 通过、退出码 0)。灵动岛各状态的窗口截图会写到
|
||||
`ISLAND_SHOTS_DIR`(默认 `.cache/island-shots/`,仓库内留档目录 `docs/screenshots/island/`),
|
||||
并对尺寸、宽高比、绘制内容(不透明像素/颜色种类)、紧急态红色像素与外溢红光做断言。
|
||||
冒烟进程使用独立 userData(`ELECTRON_SMOKE_PROFILE`),因此**用户开着客户端也能跑验证**。
|
||||
@@ -889,14 +891,14 @@ pnpm db:migrate && pnpm db:seed
|
||||
|
||||
当前实测:
|
||||
|
||||
| 验证 | 结果 |
|
||||
| --------------------------------------------------------------------------------------------------- | ----------------------------------- |
|
||||
| `pnpm verify:e2e`(开发环境与**安装后的生产实例**各跑一次) | **133/133** ✅ |
|
||||
| `pnpm verify:web`(Web 管理端真实点击 + 权限入口隐藏 + 手机适配 + 叫人 + 成绩/时间配置导入 + PWA) | **19/19** ✅ |
|
||||
| `pnpm verify:desktop`(客户端冒烟 + 开合不震动 + WinIsland 风格 + 个性化全参数 + 托盘与退出无残留) | **53/53** ✅ |
|
||||
| `pnpm typecheck` / `pnpm lint` / `pnpm format:check` | 全部通过 ✅ |
|
||||
| 安装程序完整生命周期(静默安装 → 启停脚本 → 卸载) | 通过 ✅ |
|
||||
| Docker / Nginx 部署样例 | 文件已提供,本机无 Docker 未实测 ⚠️ |
|
||||
| 验证 | 结果 |
|
||||
| -------------------------------------------------------------------------------------------------- | ----------------------------------- |
|
||||
| `pnpm verify:e2e`(开发环境与**安装后的生产实例**各跑一次) | **133/133** ✅ |
|
||||
| `pnpm verify:web`(Web 管理端真实点击 + 权限入口隐藏 + 手机适配 + 叫人 + 成绩/时间配置导入 + PWA) | **19/19** ✅ |
|
||||
| `pnpm verify:desktop`(客户端冒烟 + WinIsland 架构/开合不震动 + 个性化全参数 + 托盘与退出无残留) | **54/54** ✅ |
|
||||
| `pnpm typecheck` / `pnpm lint` / `pnpm format:check` | 全部通过 ✅ |
|
||||
| 安装程序完整生命周期(静默安装 → 启停脚本 → 卸载) | 通过 ✅ |
|
||||
| Docker / Nginx 部署样例 | 文件已提供,本机无 Docker 未实测 ⚠️ |
|
||||
|
||||
## 常见问题(本机环境已知坑)
|
||||
|
||||
|
||||
Reference in new issue
Block a user