gxpppp/dsh-boot-anim ↗★ 0

@local/dsh-boot-anim

DSH 桌面端启动动画(SteamOS 风格分镜)与 UI 分层入场过渡。纯宿主侧 index 注入,无 HTTP 路由、无源码改动。 适合追求极客启动动效、想要美化桌面端软件加载过程的用户。

パッケージ
@local/dsh-boot-anim
互換性
未検証
バージョン
1.0.0
ライセンス
MIT
最終更新
2026/10/03

インストール

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:gxpppp/dsh-boot-anim

ドキュメント

README 全文を読む ↗

DSH 桌面版启动动画 + UI 入场过渡

SteamOS 风格的七段启动分镜,以及 DSH 桌面端 UI 的「由外向内、环环平移」入场过渡。 零依赖 · 零源码改动 · 可一键还原。

启动动画演示

↑ 黑屏 → 鲸鱼 → 双线内合 → 轮廓勾勒 → 中线展开 → 落白 → UI 环环入场(约 5.6 秒,此处 10fps 采样)


目录


为什么这样做

DSH 桌面版运行时只加载 app.asar;resources/app/ 那个看似「解包副本」的目录根本不会被加载。 所以能改的地方只有三条路,本方案选第三条:

候选为什么不选
解包重打包 app.asar每次 DSH 自动更新都会覆盖;121 MB 重打包有损坏安装的风险;难以干净回滚
改 resources/app/该目录是陈旧副本,改了完全不生效
宿主侧插件 + index 注入 ✅走 DSH 官方插件契约,更新后不失效;不碰任何原始文件;卸载即还原

决定性的发现是桌面版加载链路的一个特点:

resources/app.asar/lib/main.js
  ├─ serveWebDocument()
  │    直接 readFile(dist/index.html),不调用 renderIndex()
  │    只在  里塞一行 __DSH_BOOT_READY__
  └─ DESKTOP_IPC.boot 返回 { injections, streamBaseUrl }
               ↑ 值来自 ctx.webServer.collectIndexInjections()

桌面版虽然不把注入行渲染进 HTML,但会把同一张行表通过 IPC 交给页面, 由前端解释器逐行执行。因此只要往 collectIndexInjections() 的表里 push 行, 注入就行之有效——无论内容多长,也无论有没有 HTTP 路由。

这就是宿主半侧只有 30 行的原因。


七段分镜

#画面实测时间窗
①黑屏0 – ~0.5s
②屏幕中央出现鲸鱼~0.2 – ~0.9s
③转全黑,两条线由左右两侧向中间延伸~0.95 – ~1.4s
④两线构筑成鲸鱼,并勾出与侧边栏图标一致的轮廓~1.4 – ~2.15s
—收笔后停住,等应用真正挂载~2.15 – ~3.3s
⑤中央横线向上下两侧展开,画面一分为二~3.3 – ~4.2s
⑥展开同时鲸鱼缓缓消失,露出默认白底 / 已装配皮肤~3.45 – ~4.2s
—缓冲过场:鲸鱼重组左移 → 官方字标浮现 → 光带等待后端~2.2 – ~4.2s
⑦UI 以由外向内、环环平移的方式入场~4.3 – ~5.3s
总时长≈ 5.6s

第 ④ 段收笔后不立刻揭幕,而是等应用真正挂载(最长 9 秒)。 这样慢启动不会「动画放完露空白」,快启动也不会「UI 早好了还压着黑幕」。

时长取 5.6 秒而非掌机档的 4 秒,依据是本机 Steam 客户端实测:官方桌面场景开机动画 steam_os_startup.webm = 5.000s / 1920×1200,而 deck_startup.webm 的 4.008s 只是掌机档位。


快速开始

安装

# 在本仓库根目录执行
powershell -ExecutionPolicy Bypass -File install.ps1

然后重启「DeepSeek Harness」桌面应用。

脚本会:备份 profile 的 package.json → 加一条 link: 依赖 → 往 dsh.profile.bundles 追加本包 → 在 node_modules/@local/ 建 junction。它不触碰 DSH 安装目录。

可用参数覆盖路径:

.\install.ps1 -PluginDir "D:\somewhere\dsh-boot-anim" -ProfileDir "$env:USERPROFILE\.dsh\profiles\desktop"

卸载

powershell -ExecutionPolicy Bypass -File uninstall.ps1

不重启也能看动画

无头 Chromium 跑的是同一份 CSS / JS:

powershell -ExecutionPolicy Bypass -File verify-headless.ps1

需要 Playwright。可用 -PlaywrightEntry 或 DSH_PLAYWRIGHT 环境变量指定它的位置:

.\verify-headless.ps1 -PlaywrightEntry "D:\path\to\playwright\index.mjs"

逐帧截图会写到 _verify/f*.png。


鲸鱼轮廓

需求要求轮廓与现有图标一致,做法是直接把原 path 抄过来,不是照着重画:

  • 来源:@deepseek-ai/dsh-client-ui-primitives 的 FISH_LOGO_PATH
  • 规格:3448 字符;4 条子路径(M…C…Z);viewBox="0 0 23.16 17.04"
  • 校验锚点:_verify/fish-logo-path.txt 保存逐字节副本,CI 会核对二者一致

侧边栏图标与对话区 hero 用的是同一个 FishLogo 组件,所以「与图标一致」是构造性成立的。

两个几何上的取舍

同一段 path 画两遍。 分镜 ④ 要让「两条线分别构筑」,做法是把同一段 d 渲染两次, 各套一个 clipPath:#ba-clip-l 只留 x 11.58。 两条路径的 stroke-dashoffset 反向(-1 → 0 与 +1 → 0), 于是描边从中间同时向两端铺开。

压扁时描边不能变细。 ③ 与 ④ 需要同一个元素从「一条横线」连续变形成鲸鱼轮廓: 鲸鱼组套 scaleY(0.018) 压成约 2px 的横线,再 scaleY: 0.018 → 1 长成鲸鱼。 但压扁会把描边一起压细到看不见——解法是 vector-effect: non-scaling-stroke, 描边宽度不随 transform 缩放,压平时它仍是一条实心横线。这样 ③ 与 ④ 不必切换 DOM。


缓冲过场

在「轮廓勾勒完成」与「横线展开」之间插了一段过场:鲸鱼淡出重组、左移让位, 官方字标在其右侧浮现;若后端仍在加载,一道斜向光带反复扫过字面。

字标要等鲸鱼左移进行到 82% 才浮现 —— 早于此时会被还停在中央的鲸鱼压住, 两者都是矢量、会直接叠在一起。这是逐帧实测出来的,不是估的。

字标用的是官方矢量

不是文字排版,而是直接从上游 dsh-client-ui-primitives 的 BrandWordmark 组件提取的矢量数据:18 个图元 / 13931 字符 path / 2 处裁剪框, 由 _verify/gen-wordmark.mjs 自动生成并断言结构。手抄这 1.4 万字符不可能不出错。

数据经 global 注入行送进页面 —— boot-anim.js 是注入的独立脚本,无法 import。

光带的实现

光带层是字标的同形副本,被一道移动的遮罩裁切:只有光带扫过的部分才高亮出来。

这里有两个坑,都是实测才发现的:

  • 不能用 mix-blend-mode: screen —— 屏幕混合下「白叠白」恒为白,等于没有效果;
  • 定位必须与字标层完全一致 —— 曾因写成 inset: 0 而铺满容器, 导致字标副本被放大 3.46 倍、跑到左上角(看起来像两个巨大的汉字)。

另外「加载期间压暗字标」也踩过一次:内联样式会被入场动画的 fill: 'forwards' 终值覆盖(实测 inline 0.22 而 computed 1), 必须改用 WAAPI 动画才能压下去。

详见 docs/04 第 9 节。


UI 入场过渡

没有稳定类名怎么找 UI 分区

DSH 前端是 CSS Modules(hash 类名)+ 打包产物,没有可挂钩的类名。链条是:

document.querySelector('[data-shell-overlay]')   ← AppFrame 里唯一的语义锚点
        .parentElement                            ← 就是 AppFrame 的三列 grid 容器
        .children → 按 getBoundingClientRect().left 排序
                 → 最左 = 侧边栏、中间 = 对话列、最右 = 右栏

三段波次

波次目标方向位移时长交错起始延迟
1外层三列左栏向左、右栏向右、中列向上40px460ms—0 / 70 / 140ms
2列内主块自下浮起26px380ms60ms—
3主块内内容块自下浮起16px320ms40ms—

合计约 730ms,落在「总交错 /DeepSeek Harness.exe" _verify/probe-loader.mjs

用真实 cordis 跑端到端

ELECTRON_RUN_AS_NODE=1 "/DeepSeek Harness.exe" _verify/probe-e2e.mjs


两个探针需要显式给出路径(本机会把 `HOME` / `USERPROFILE` 改写):

````powershell
$env:DSH_RESOURCES = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources"
$env:DSH_PROFILE   = "$env:USERPROFILE\.dsh\profiles\desktop"

已验证 / 未验证

已验证(无头 Chromium 154,跑的是即将上线的同一份 CSS / JS):

  • 七段分镜顺序与时序,18 个采样点覆盖全部镜头
  • 第 ④ 段鲸鱼轮廓可辨识,与侧边栏图标是同一条 path
  • 舞台自毁、无 DOM 残留、无标记残留、控制台零报错
  • UI 三段波次目标数 {cols: 3, inner: 5, deeper: 2}
  • 真实加载器:25 个 bundle 全部接受、0 跳过
  • 真实 cordis 端到端:12/12 断言通过
  • 安装 / 卸载往返:profile 正确改写与回滚,无 BOM

未验证(如实标注,不从等价验证外推):

  • 真实 DSH 桌面窗口中的最终观感 —— 需重启桌面版
  • 真实桌面版的 IPC 往返时机 —— 同上

参考来源

完整清单见 docs/01-方案检索清单.md。核心来源:

SteamOS 侧的一手数据来自本机 Steam 客户端的启动动画文件元数据实测,见 docs/01-方案检索清单.md 第 0 节。


许可

MIT