Initial commit: RHINE LAB · ANALYSIS OS:三维界面与动效实验(本仓库不含个人归档索引数据)
@@ -0,0 +1,33 @@
|
||||
node_modules/
|
||||
backups/
|
||||
dist/
|
||||
.tools/
|
||||
*.mp4
|
||||
*.blend1
|
||||
optix7cache.db
|
||||
reference/*.jpg
|
||||
reference/**/*.jpg
|
||||
art/.cache/
|
||||
.worktrees/
|
||||
release/
|
||||
video/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
reference/local-files-connection.js
|
||||
|
||||
# Local integration checkout
|
||||
.main-integration/
|
||||
|
||||
# Individually licensed MyFonts kit; install locally, never publish to Git.
|
||||
public/fonts/novecento/
|
||||
.vercel/
|
||||
|
||||
# 本地归档服务每次启动生成的访问令牌
|
||||
.archive-token
|
||||
|
||||
# 一键启动脚本产生的运行日志与 PID 记录
|
||||
logs/
|
||||
@@ -0,0 +1,369 @@
|
||||
# Rhine Lab UI
|
||||
|
||||
## 索引与阵列:当前机制(权威,动它之前先读这一节)
|
||||
|
||||
> 下方时间线是历史记录,其中有几段**已经被回退**。读到时以本节为准。
|
||||
> **不存在**的文件(曾在时间线里出现,现已删除,不要再去找):
|
||||
> `src/archive-array.ts`、`scripts/check-array.mjs`;`src/data.ts` 里的
|
||||
> `loadArchiveDataset` / `searchDataset` / `columnCount` / `recordKey` / `indexOfKey` /
|
||||
> `childrenOfPath` / `datasetSource` / `laneCenter` / `rowPeriod` 也都已移除。
|
||||
|
||||
**数据流**:`content/curated.mjs`(手写)→ `npm run archive:snapshot` → `content/archives.json`(生成物)
|
||||
→ `src/data.ts` → 阵列(`src/archive-loop.ts` / `src/archive-visibility.ts` / `src/scene.ts`)。
|
||||
`public/archive-index.json` 只服务 LOCAL ARCHIVE 面板与检索,**不参与阵列**。
|
||||
|
||||
**现状**:5 个分类,每列条数**不限**(校验器只要求每列 ≥1、总数 ≥5),当前内容 **47 条**,五列 **8 / 9 / 11 / 9 / 10**(刻意不等长,用来证明上限确实放开了)。
|
||||
阵列窗口是 32 行、内容从第 12 行起编号,`slot = lane * 32 + (row % 32)`。
|
||||
|
||||
### 想改数量时动哪里
|
||||
|
||||
| 目标 | 动这里 | 别动 / 注意 |
|
||||
| --- | --- | --- |
|
||||
| 每列条数、总条数 | `content/curated.mjs` 增删条目 → `npm run archive:snapshot` | **不要手改 `content/archives.json`**,它是生成物,下次生成会覆盖 |
|
||||
| 单列超过 20 条 | 不用改任何东西 | 交互路径的行空间无上限(`fileAtCell` 按 `files.length` 取模循环);`slot` 已用 `% 32` 防止串到下一列 |
|
||||
| 分类数量 5 → n | `curated.mjs` 的 `categories`/`columns` **+** `scripts/archive-content.mjs` 的 `names.length !== 5` **+** `src/archive-loop.ts` 的 `POOL_LANES` / `LOOP_COLUMNS` **+** 行内写死的列原点 `(lane - 2)`、`/ 5`、`/ 8`(`archive-visibility.ts:36,42`、`scene.ts` 的相机/动效/取值多处) | ⚠️ 列数一改就动了**原片逐帧对照**的构图基准,必须重新校准;不要只改一处 |
|
||||
| 刻度条长度 | `src/main.ts` 的 `TICK_WINDOW` | 短列必须走自然顺序分支(见下) |
|
||||
| 校验规则 | `scripts/archive-content.mjs`;同步改 `scripts/check-content.mjs` 的 `invalidCases` | 校验不过时 `predev`/`prebuild` 会**直接 throw**,dev server 和 build 都起不来 |
|
||||
|
||||
### 改完怎么自检(改了阵列/内容/场景就跑这一套)
|
||||
|
||||
```powershell
|
||||
npx tsc --noEmit # 类型必须 exit 0
|
||||
npm run check:content # 档案数据校验,20 项
|
||||
node --experimental-strip-types scripts/check-archive.mjs # 相机/抽取几何
|
||||
node --experimental-strip-types scripts/check-loop.mjs # 循环阵列
|
||||
npm run build # prebuild 会跑快照+校验+字体
|
||||
```
|
||||
|
||||
⚠️ `check:archive` / `check:loop` **没有** npm script(只有 `check:content` / `check:viewport` 有),
|
||||
必须用上面的 node 形式跑文件。`check:array` 已随方案回退一并删除,**不要再引用**。
|
||||
|
||||
### 已经踩过的坑(改之前先看)
|
||||
|
||||
1. **别把每列条数写死成 8。** 旧版校验器与阵列数学都假定每列恰好 8 条;用户要求"每列无限"后已放开,但阵列窗口只有 32 行、内容从第 12 行起 → 每列**超过 20 条**时若 `slot` 不取模就会串到下一列的行上。`data.ts` 的 `slot: lane * 32 + (row % 32)` 就是为这个加的。
|
||||
2. **刻度条窗口化时,短列必须保持自然顺序。** `main.ts` 的 `tickWindow()` 里 `const start = len <= TICK_WINDOW ? 0 : position - Math.floor(count / 2)`。若一律"以选中项居中",列内 8 条时窗口会绕回来变成 `5,6,7,8,1,2,3,4`——顺序整体转位、高亮卡在正中,看起来就是"滑条坏了"。这个 bug 已经发生过一次。
|
||||
3. **`fileAtSlot` 是截断、`fileAtCell`(archive-loop)是循环取模**,两者语义不同,不要合并。`fileAtSlot` 服务 boot 与原片对照的固定 160 布局,必须与原版一致。
|
||||
4. **别再用"往阵列里塞全量归档文件"的方案。** 试过一次(每个文件一张盘),造成检索列表里全是 `00_索引` 的 CSV/MD/TXT、选中列外条目还会错位到第一列的盘上,用户判定"索引完全改坏",已整体回退。要扩内容请往 `curated.mjs` 加条目。
|
||||
5. **`records` 是静态 import**,模块顶层就会用到它(`main.ts` 里 `columnFiles(...)[0]`、`fileLocation(selected).lane`)。若日后要改成异步加载,必须先处理这些顶层访问,否则启动即崩。
|
||||
|
||||
### 开场与退出的入口序列
|
||||
|
||||
`src/main.ts` 的 `beginOpening()`:加载完成 → 尝试解锁音频 → logo 停留 `ENTRY_LOGO_HOLD = 1000ms` → 渐出 → 自动播放(**没有点击门槛**,`src/startup.ts` 的 `StartupGate` 已无调用方)。
|
||||
浏览器要求用户手势才允许启动音频设备,解锁失败时本次开场静音,但**不清空** `prefs.sound/music`,改由 `armAudioUnlock()` 在首次 `pointerdown`/`keydown` 补解锁。
|
||||
右上角那个 46px 圆形按钮:开场中是 `↗` 跳过(`.skip`),进入档案/详情后变成 `⏻` 退出(`.exit`),两者同坐标不重合。
|
||||
退出序列(`exitSystem()`,CSS 时序在 `style.css` 的 `.shutdown` 段):白屏 + logo(**不闪动**)→ 字符本体明灭(`shutdownFlicker`)→ 黑条从左扫到**完全覆盖**为止、**不向右滑出**(`shutdownSweep` 无 100% 那一段)→ `SHUTDOWN_WINDOW` 之后 `window.close()`,关不掉则退化为纯白页。
|
||||
|
||||
- 2026-09-11:官网已独立集成 Novecento 开场字体;授权 kit 不进入 Git。正式 Vercel 项目在构建时从当前生产版本恢复并校验字体,首次部署使用本机授权 kit 的静态输出。见 verification/WEBFONT-DEPLOYMENT.md。
|
||||
|
||||
## 开场中央字体
|
||||
|
||||
- 2026-09-11:用户要求中央开场文案中适合的位置统一使用 Novecento Sans Wide。访问、身份、请求、处理、授权提示使用 Normal;欢迎三行使用 Bold。后续按用户纠正与原片叠加比较,左上 RHINE LAB 使用 DemiBold,下方两行保留 MiSans。沿用原时间轴与 HUD。
|
||||
- 用户后续授权从 MyFonts 领取 Normal、DemiBold、Bold 正式 Webfont 并直接替换,本机已领取并接入。public/fonts/novecento 为独立许可资源,排除 Git;有完整字体包时使用原始 WOFF2,其他检出或加载失败保留既有固定图形。来源、再制作与回归见 verification/BOOT-LETTERING.md。网页年度许可不等于已确认创意工坊分发授权,本轮未上传。
|
||||
|
||||
## Wallpaper Engine 实验分支
|
||||
|
||||
- 用户追加授权音乐律动、手动点击开始且可隐藏入口的波纹接力小游戏,以及入场详情开关。工作台默认短入场,展示模式默认完整;可强制覆盖。工作台内容轻微错峰淡入,减少动态效果时直接显示。
|
||||
- WE 属性采用原生可折叠分组和条件显示;选中档案支持保持原样、仅音乐时归齐、始终归齐。原生实现继续沿用,不启用前端或动效 Skill。详见 docs/WALLPAPER-ENGINE.md。
|
||||
- 用户进一步要求登录入场适配多分辨率并移除底部字幕。正常入场背景与三维画布铺满实际视口,中央图形保持比例;只有显式逐帧对照保留固定 1920×1080 舞台。此前“所有开场固定比例”规则由此更新。
|
||||
|
||||
- 用户于 2026-09-10 授权新建 `codex/wallpaper-engine`,在本机 Wallpaper Engine 中试用网页壁纸。当前作为独立实验保留,不合并主分支、不上传创意工坊。
|
||||
- 沿用现有原生实现和视觉规范;`npm run build:wallpaper` 输出 `release/wallpaper`,网页正式构建保持原流程。
|
||||
- 壁纸资源全部本地打包,关闭 PWA,自动启动,接入宿主设置、帧率和暂停通知。使用说明与实际验证见 `docs/WALLPAPER-ENGINE.md`。
|
||||
|
||||
- 用户明确要求:以目录内视频 5–40 秒为视觉与动效参考,尽量逐像素还原;不使用前端 Skill,由代理选择技术栈。
|
||||
- 本项目采用参考视频逐帧分析与原生实现流程,不启用主前端设计或动效 Skill。
|
||||
- 美术资源必须通过 Blender MCP 制作,保留源文件与可复现脚本。
|
||||
- 技术栈:TypeScript、Three.js、Vite。1920×1080 为布局基准,等比例适应窗口。
|
||||
- 视觉与时间轴规范见 DESIGN.md。视频仅用于参考与验证,不能作为产品交互背景。
|
||||
|
||||
## 阵列拖动与持续惯性
|
||||
|
||||
- 用户要求悬停轻抬,拖动浏览阵列,鼠标滚轮切换列内档案;鼠标与触摸共用原生输入逻辑。
|
||||
- 用户进一步明确需要类似网页快速滑动的持续惯性:快拖与慢拖按实际松手速度产生不同滑行距离,不能采用固定最多两列/三档的追加距离。松手后持续减速,档案随经过的位置依次切换,接近停止时才吸附;再次按住从当前画面接管。
|
||||
- 用户最新明确选择自由平面拖动:用当前相机的两条投影轨道共同还原屏幕位移,取消横竖快捷手势和斜向锁轴。一次拖动可随时转弯或反向,两个轨道共同衰减并最终吸附。拖动按实际物理位置选择档案并更新列内记忆;键盘和按钮切列继续恢复该列记忆。
|
||||
- 已确认的波浪、竖直抽取、归位、列内记忆与循环规则继续沿用。实现与验证见 `verification/ARRAY-INPUT.md`。
|
||||
|
||||
## 详情与弹窗过渡
|
||||
|
||||
- 详情退出约 180ms,快速返回再进入从当前透明度接续;背景遮罩随实际镜头进度变化。抽取、转正、归位与波浪继续沿用已确认的三维规则。
|
||||
- 检索、收藏和设置弹窗进入约 300ms、退出约 200ms。退出期间保留节点与背景输入隔离,结束后恢复焦点,再执行选档或重播。
|
||||
- 详情页签使用 150ms 内容淡入与 180ms 下划线移动;收藏仅更新按钮状态、总数和反馈,保留正文节点、页签及滚动位置。减少动态效果时直接切换。验证见 `verification/UI-TRANSITIONS.md`。
|
||||
|
||||
## 标注修订约束
|
||||
|
||||
- 用户标注对应原片约 27.60、29.16、31.56 秒;相机校准同时检查端面厚度、空隙和前后排边界。
|
||||
- 同一档案从阵列升起并进入特写时,仅允许竖直升降,不能把模型向前或横向平移来重新构图;靠近与转向由摄像机完成。初始整组阵列入场及切换档案的轨道移动不属于抽取动作。
|
||||
- 背景卡片不能随特写进度额外下沉来让出画面。
|
||||
- 交互模式左右切换五类档案列,上下切换同一列的前后档案;每列至少 8 份可读档案。切列保留该列的选择记忆。
|
||||
- 预览时卡片抬起 0.4;交互详情升至 4.05(原片时间轴仍为 3.35),只比早先略高,必须保留后方阵列的可见层次。镜头负责左侧构图,右侧展示信息。
|
||||
- 卡片获得净空后才允许拖拽旋转;收回时保持高度先转正,再下降。被换选的旧卡片也遵守此顺序。
|
||||
- 阵列与抽出卡片的材质、内部细节必须连续过渡,快速切回正在归位的卡片时保留当前升降状态。
|
||||
- 标签复用主界面的同一份莱茵生命标志,不使用另画的简笔版本。
|
||||
|
||||
## 独立模型查看器
|
||||
|
||||
- 用户要求在详情页增加 360° 模型查看、方向键平移、滚轮缩放、拆解和一键重组,先提供可体验的版本。
|
||||
- 独立查看器沿用既有视觉规范与原生实现流程;部件模型通过 Blender MCP 制作,保留源文件与生成脚本,见 DESIGN.md。
|
||||
- 阵列中的抽取与归位继续遵守上文约束。独立查看器在自己的场景内旋转观察与分层拆解,关闭后回到原档案。
|
||||
|
||||
## 循环档案阵列
|
||||
|
||||
- 用户确认:主界面的档案阵列上下切档案、左右切列均可持续循环,画面不出现队列尽头。
|
||||
- 五类档案、每类八份的内容顺序循环复用;跨首尾仍沿按键方向移动到相邻位置,切列保留该列的档案选择记忆。
|
||||
- 使用固定的可见窗口与外围卡片补位。归位副本按实际循环位置区分,保留快速重新选中时的运动状态。
|
||||
- 原片参考时间轴继续使用原来的五列、160 个位置;循环只用于交互阵列。相关实现与验证见 DESIGN.md、verification/LOOPING-ARCHIVE.md。
|
||||
|
||||
## 当前动效选择与光影微调
|
||||
|
||||
- 用户试用后明确选择原始基线 aeddd8f:选中抬起和波浪同时开始,恢复原有负向波谷与 4.2 阻尼速率。此前去波谷、先抬起后波浪及源点静止方案只保留为对照实验,不用于主界面。
|
||||
- 用户对比后选择恢复整体基线的颜色与光照。生产场景及独立查看器默认采用 baseline 配置;光影微调只保留为对照实验。见 DESIGN.md。
|
||||
- 档案序号和列编号使用 Rolling Number,保留两位数;X-001 形式的档案编号与悬停提示使用三位数滚动,X- 前缀固定。连续输入延续当前运动,减少动态效果设置关闭滚动。
|
||||
|
||||
## 标题与查看器过渡
|
||||
|
||||
- 主界面随选档变化的顶部分类、底部列名、悬停标题与权限标签统一复用主标题的文字滚动。相同内容不重复播放,悬停首次出现直接显示;跨列刻度复用固定八个按钮,保留连续伸缩和各列选档记忆。见 `verification/ROLLING-TITLE.md`。
|
||||
- 用户最新选择:主档案标题改为类似数字的滚动文字,替代此前快切黑条。直接复用已有库的 `createRollingText`,与编号共享 460ms 时长;使用 direct 模式、各字同时启动、轻微模糊与宽度过渡。文字固定向上滚动,快速输入衔接最新目标;进入详情或减少动态效果时直接显示最终标题。见 `verification/ROLLING-TITLE.md`。
|
||||
- 360° 独立查看器增加进出过渡。进入约 320ms 淡入,模型载入后约 380ms 轻微展开;退出约 220ms 淡出。退出结束前保留模型与模态焦点,结束后释放并返回原按钮;进入中途按 Esc 可衔接退出。
|
||||
|
||||
## 前段 2D 动效逐帧修订
|
||||
|
||||
- 用户明确范围:黑底的鹰角网络片头不做,直接从画面变白开始。正常启动和重播从原片 6.76 秒进入;本地对照保持原片时间码。
|
||||
- 继续使用逐帧分析和原生 DOM/SVG 实现,不启用前端或动效 Skill。2D 时间轴见 `src/boot-motion.ts`,渲染见 `src/boot.ts`;规范与复核见 `verification/BOOT-MOTION.md`。
|
||||
- 已认可的三维交互、基线颜色和档案阵列时间轴继续沿用。启动标志沿共享标志的同一组贝塞尔曲线连续绘制,缺口通过路径首尾控制,保留与档案标签一致的轮廓。
|
||||
- 用户进一步要求按原片逐帧修订非线性运动。左上三行分别在 278、280、282 帧滑入,各自加速后减速;圆环半径、弧端、内环与橙点使用独立轨迹,名称黑底快速扫过后长尾收束。测量轨迹见 `src/boot-tracks.ts`;局部放大对照及限制见 `verification/BOOT-MOTION.md`。
|
||||
- 用户进一步强调两侧小圆主要绕自身圆心旋转并补全弧段;仅保留原片测得的轻微位置收拢。中央大点闪变后缩小,六颗小点错峰绕行。Logo 以连续笔画首尾及移动缺口表现运动,不能替换成整枚标志闪烁;加号与方点同帧展开,方点随后拉成减号。见 `src/boot-orbit-tracks.ts`、`src/boot-logo-tracks.ts`。
|
||||
|
||||
## 品牌文字与 360° 导航过渡
|
||||
|
||||
- 用户要求放大并叠加原片,校准左上角三行文字。保留 MiSans,三行约 270px 宽;中间一行使用 600 字重,ANALYSIS 按原片字母位置分别校准间隔,OS 右对齐。具体尺寸见 DESIGN.md,叠加页为 `reference/brand-review.html`。
|
||||
- 360° 查看器的平移、滚轮及加减键缩放均平滑跟随;复位约 560ms,按球面短路径返回。复位过程中输入从当前显示位置接管;减少动态效果时直接到位。仅用于独立查看器。
|
||||
## 声音实现
|
||||
|
||||
- 用户要求依据原视频与莱茵生命相关设定重设计音效和背景音乐。本次实现采用原创三轨循环配乐和 Web Audio 音效,详见 `verification/AUDIO-DESIGN.md`。
|
||||
- 音效与音乐独立开关、独立音量;保留旧静音偏好。启动声音沿用原片时间码,跳帧不补播;三轨在切场景时连续播放并改变比例。
|
||||
- 听感尚待用户试听,技术验证不视为用户已经确认声音方向。源谱与音色生成脚本必须保留。
|
||||
- 用户进一步指定玻璃交互应采用清脆音色。当前音效使用薄玻璃轻碰与短共鸣,切档、抽取、归位及拆组有不同的衰减;保留快速输入时的叠音限制。用户尚未确认本版最终听感。
|
||||
- 用户明确要求复刻原视频已有的逐字输入音效。输入现在使用三个原片短音(各 38ms),保留来源记录;其他卡片交互沿用玻璃音色。输入节奏跟随真实文字揭示,不把故障闪动恢复当作新输入。
|
||||
- 用户接受档案切换使用玻璃声,并要求检查 OS 页面打开、Logo 与文字出现的声音。窗口开合、系统操作、启动标志、整块文字及验证反馈采用独立电子音色;玻璃声用于档案实体运动。完整开场试听见 `reference/boot-audio.html`,只临时开启声音,结束后恢复偏好。具体音色尚待用户试听。
|
||||
|
||||
## 档案内容维护
|
||||
|
||||
- 档案内容独立保存在 `content/archives.json`,页面和 TXT 导出共用;修改步骤见 `content/README.md`。五列保持不变,**每列条数不限**(2026-09-12 起;校验器只要求每列 ≥1、总数 ≥5),编号 `X-0NN` 按顺序稳定、宽度随总量自适应,构建前校验;内容按纯文本显示。
|
||||
- 2026-09-12:`archives.json` 已改为**生成物**,手写内容在 `content/curated.mjs`,由 `scripts/archive-source.mjs` 扫描 `E:\归档` 后写出,直接编辑 JSON 会被下次生成覆盖。五列、每列八份、`X-0NN` 稳定编号的约束不变,校验规则仅在 `source` 上额外接受 `file:` 链接。
|
||||
- 同期新增本地归档接入:**LOCAL ARCHIVE** 面板检索归档全部条目并执行移动/复制/解压/新建目录等安全写操作,由只监听回环、带每次启动令牌的 `scripts/archive-service.mjs` 承担;归档变动会自动重建快照。使用与安全边界见 `docs/LOCAL-ARCHIVE.md`,实现与实测证据见 `verification/ARCHIVE-INTEGRATION.md`。该面板与四十条阵列互相独立,阵列的数学与三个消费方(`src/scene.ts`、`src/archive-loop.ts`、`src/data.ts` 的位置映射)未改动。
|
||||
- 2026-09-12:用户要求把详情页的 EXPORT 按钮(原为下载 TXT)改为直接打开本地文件资源管理器中的文件位置。现为 **SHOW IN FOLDER / 文件位置**,用该档案的真实 `path` 调 `POST /reveal`;服务不在线时提示启动方式而非静默失败。`public/archives/*.txt` 仍会生成但详情页不再提供下载入口。同时修掉详情页脚注的 `file:` 参考链接(浏览器不允许从网页跳转 `file:`),改为显示归档内相对路径文本。
|
||||
- ⚠️ **以下四条(全量归档驱动 → 修正盘的粒度 → 独立详情导航 → 文件夹即列)已于 2026-09-12 当天全部回退。** 留在时间线里只作历史与教训,**不要再按它们实现**;当前机制以本文件顶部「索引与阵列:当前机制(权威)」为准,回退范围见下文「索引与阵列方案整体回退」。
|
||||
- 【已回退】2026-09-12:**三维阵列改为全量归档驱动**(用户明确选择"真正的全量阵列")。列 = 归档顶层内容分类(当前 7 列),列内是该分类下全部真实归档条目(合计 49,997 条);标题/摘要/研究记录由索引自动派生,用 Proxy 惰性物化,只给被读到的条目生成文本。四十条手写精选档案降级为**备用数据集**(索引不可用时兜底,应用照常启动)与 TXT 摘要来源,因此 `archive-content.mjs` 的校验器与 `check-content` 的约束**保持不变**。
|
||||
- `data.ts` 重写为双数据集 + O(1) 预计算寻址(`laneMembers` / `laneOf` / `posOf` / `counts`)。旧实现每次调用全量扫描 + `indexOf`,全量下每次输入事件约 19 ms、一次拖动累计约 1.2 秒(注意:**不是**每帧开销,渲染循环不碰 `records`)。改造后 2000 次寻址 38,365 ms → 0.33 ms。
|
||||
- 引入**全局行周期**(`rowPeriod()` = 最长一列的条数)解决 rebase 取整:各列长度不同,不存在公共周期,rebase 位移必须是全局周期的整数倍才能保持内容不变。
|
||||
- `fileAtSlot`(**截断**,服务 boot 与 `?time=`/`review=1` 逐帧对照的固定 160 布局)与 `indexAtCell`(**循环取模**,服务交互阵列)语义不同,不可合并。
|
||||
- 收藏改按归档相对路径键存放(`recordKey`),否则新增文件导致编号位移会让旧收藏指向别的档案。`#file-ticks` 改为固定 8 个按钮的滑动窗口,避免单列上万条时渲染上万个 DOM 节点。
|
||||
- 列数参数化:`archive-loop.ts` 新增 `configureLoop()`(`LOOP_COLUMNS`/`POOL_LANES` 改为 live binding,前五项恒为真实列以保证固定 160 布局成立);`archive-visibility.ts`、`scene.ts` 的 lane 原点改用 `laneCenter()`;`motion.ts` 增加 `laneOrigin` 可选参数。`fixed` 路径保持 5 列不变(原片对照基准),正常入场走动态路径,因此阶段之间不跳变。
|
||||
- 新增自包含回归检查 `npm run check:array`(桩掉 fetch 直读快照,可进 CI),断言反向寻址一致、格子互为逆、rebase 平移不变性、列循环回绕、检索不物化、寻址吞吐。设计与证据见 `verification/ARCHIVE-ARRAY.md`。
|
||||
- 远距离跳转不再长距离飞行:`scene.ts` 的 `ROW_SNAP_DISTANCE = 64`(可视窗口 32 行的两倍)。超长列上 `nearestOccurrence` 最远会给出半列(约 10,337 行)的目标,超过阈值就让 `shoulder`/`rail` 直接落位、速度清零;只作用于行轴,列轴最远半个列数、保留原动画,拖动/惯性/抽取期间显式排除。阈值两侧由 `check-array` 用真实数据钉住。
|
||||
- 已知未决:7 列下的实机构图与剔除数值需人工确认(无浏览器自动化环境)。
|
||||
- 【已回退】2026-09-12:**修正盘的粒度**——用户指出"一个项目就是一个 3D 盘,在盘内的页面可以自由访问所有文件"。此前把**每个文件**都做成一张盘(49,997 张)是错的,会导致列表里全是 `00_索引` 的 CSV/MD/TXT、选中这类列外条目还会错位到第一列的盘上。
|
||||
- 现在 **盘 = 分类下的第 1 层目录**(项目单元),文件与更深的目录都不是盘。列分布变为 `4 / 2 / 11 / 3 / 4 / 4 / 4 = 32 张盘`,列均衡、可浏览。
|
||||
- 列外条目(`laneOf < 0`)**不再参与** `searchDataset`,也不会出现在检索列表里——它们的正确入口是所属盘的页面,或 LOCAL ARCHIVE 面板。
|
||||
- 未输入关键字时,检索列表改为列出阵列成员(各列的盘),不再退回 `records` 开头若干条(那正是元数据文件)。
|
||||
- 详情页新增 **04 盘内文件** 页签:列出当前盘的直接子项(目录在前),目录可逐层进入并带面包屑返回,文件可直接【打开】【定位】。子项表在 `archive-array.ts` 里惰性建一次(一趟遍历),之后 O(1)。
|
||||
- 副作用:`ROW_SNAP_DISTANCE = 64`(远距离跳转落位)在盘模型下**处于休眠**——最长列只有 11 张盘,最近同现点最远 5 行。阈值保留以应对日后粒度变化,`check-array` 会报告 `snapEngaged` 的值。
|
||||
- 2026-09-12:用户要求**开场播完直接进入浏览,不要自动展开头一张盘**。原先 `main.ts` 的 `bootFrame` 在原片时间轴走完(t ≥ 35)无条件 `setMode("detail")`,而「入场详情」属性只在 `isWallpaper` 分支里被读过——网页版没有开关,永远自动展开选中的头一张盘。
|
||||
- 现在该处改为:网页版 `setMode("archive")`;壁纸版仍按 `openingShowsDetail(openingdetail, workbench)` 决定是否展开,行为不变。
|
||||
- `?scene=detail`、`?time=`/`review=1` 逐帧对照(frozen time 恒 < 35)、以及"减少动态效果"直进阵列等既有路径都不受影响。
|
||||
- 2026-09-12:用户要求**去掉开场的"点击进入"门槛、启动即自动播放**,并**把 ENTER SYSTEM 做成右上角不起眼的圆形按钮**,以及 logo 先显示 1 秒再渐出。
|
||||
- 开场入口序列改为 `beginOpening()`:尝试解锁音频 → logo 停留 `ENTRY_LOGO_HOLD = 1000ms` → 渐出 → 自动 `completeStartup()`。`StartupGate` 不再被引用(`src/startup.ts` 保留但已无调用方)。
|
||||
- **音频的现实约束**:浏览器要求用户手势才允许启动音频设备,自动开场时 `unlock()` 可能失败。此时本次开场静音,但**不再清空 `prefs.sound/music`**(原先 `completeStartup(silent)` 会写死静音偏好,自动开场下那样做等于擅自关掉用户的设置);改为 `armAudioUnlock()` 在首次 `pointerdown`/`keydown` 时补解锁,音乐与后续交互音效接上。开场自身的音效在那种情况下会是静音——这是浏览器策略,不是缺陷。
|
||||
- `completeStartup()` 去掉了 `silent` 参数;`?time=`/`?review=1` 逐帧对照不加 1 秒停留,避免影响对照工具。
|
||||
- `.skip` 由文字按钮改为 46px 圆形(`right: 38px; top: 30px`,`opacity: .3`,hover/focus 提到 1),`data-action="skip"` 不变;不再在开场结束后抢焦点。移动端的 `.mobile-entry` 保持原样。
|
||||
- 【已回退】2026-09-12:用户指出**盘内的文件打不开详情页**——只能看盘(列内成员)的详情。原因是详情页绑在阵列选中项 `selected` 上,而列外的文件/子目录没有盘位(`laneOf < 0`),`fileLocation` 会退化成第一列的盘。
|
||||
- 引入**独立的详情页导航状态** `detailEntry`(-1 = 跟随阵列选中项),`detailIndex()` 统一取当前应展示的条目。`renderDetail` / `overview` / `setTab` / `contentTab` / `toggleSaved` / `revealSelectedArchive` / 页脚编号全部改用它;`updateSelection` 仍用 `selected`(它渲染的是阵列选中盘的界面)。
|
||||
- 新增 `openEntryDetail(index)`:详情页可切换到**任意**条目(盘或盘内文件/目录)。盘内文件列表与检索命中里都加了【查看】按钮;面包屑逐级可点,点上级目录即打开那一级的详情页,实现"返回上级"。
|
||||
- `select()` 与 `setMode("archive")` 都会把 `detailEntry` 复位为 -1,避免回到阵列后再进入时还停在上次看的那一层。
|
||||
- 顺带修正:`SHOW IN FOLDER`(`revealSelectedArchive`)现在作用于**当前查看的条目**,因此盘内任意文件都能直接在资源管理器中定位。
|
||||
- 【已回退】2026-09-12:用户进一步明确阵列层级——"**每一个文件夹一个横列,里面的每一个文件就是一个盘;子目录也算盘,可以在详情里继续查看子目录里的文件**"。
|
||||
- 现在 **列 = 分类下的第 1 层文件夹**(32 列,按路径排序);**列内成员 = 该文件夹的直接文件(盘)+ 直接子目录(项目,同样是盘)**。子目录盘点开后进详情页,用「盘内文件」页签继续往下看,可逐层进入。
|
||||
- **为什么子目录也要进列**:只放文件的话,`02_游戏/Minecraft` 这类只有子目录的文件夹会既不是列、也不是盘 → **从阵列里彻底消失**(实测 32 个文件夹里有 11 个是这种)。把子目录也算作盘之后 **空列为 0**,所有文件夹都在阵列里可达。
|
||||
- 实测分布(`check:array`):32 列 / 435 张盘 / 0 空列;最多 251、中位 2、最少 1;10 列只有 1 张盘。列内循环由 `indexAtCell` 的取模保证(到末尾回到第一张)。
|
||||
- 修复一个被 32 列暴露的真 bug:`archive-loop.ts` 的 `poolCell()` 直接取 `POOL_LANES[floor(index/LOOP_ROWS)]`,而 `selectedSlot = laneCenter()*32+12` 在 32 列下是 508 → 下标 15 越界(`POOL_LANES` 只有 9 项)→ 模型坐标变 `NaN`。已改为按 `POOL_LANES.length` 回绕。
|
||||
- 副作用:`ROW_SNAP_DISTANCE = 64` 的保护**重新生效**(最长列 251 张盘,半列跳转 125 行 > 64,`snapEngaged: true`)。
|
||||
- 2026-09-12:**索引与阵列方案整体回退 + 每列条数放开**(用户原话:"你把索引机制完全改坏了,直接退回到原版")。上一条 ⚠️ 标注的四条全在此次回退范围内,本节是它们的收尾。
|
||||
- **删除**:`src/archive-array.ts`、`scripts/check-array.mjs`,以及 `package.json` 里指向它的 `check:array` 脚本项(那个文件没了以后 `npm run check:array` 只会报缺文件)。**还原**:`src/data.ts` 回到 38 行原版(`columnFiles` / `fileLocation` / `fileAtSlot`);`src/archive-loop.ts`、`src/archive-visibility.ts`、`src/scene.ts` 的 `laneCenter()` / `configureLoop()` / `rowPeriod()` / `ROW_SNAP` / lane 原点全部回到原版;`main.ts` 里依赖它的 `detailEntry` 独立导航、盘内文件页签、检索分支一并移除。**保留**(用户单独要求过,与阵列无关):LOCAL ARCHIVE 面板、`POST /reveal`、SHOW IN FOLDER、`beginOpening()` 自动开场、圆形 `.skip`、boot 结束进 `archive`、退出系统。
|
||||
- **每列条数已放开到不限**:`scripts/archive-content.mjs` 只要求"每列 ≥1、总数 ≥5"(原为恰好 5 列 × 8 条),`idWidth = Math.max(3, String(records.length).length)` 让编号宽度随条数自适应(1200 条时是 `X-0001` 而不是溢出到 `X-1000` 变四位)。`data.ts` 的 `slot: lane * 32 + (row % 32)` 是配套的防串列护栏(见顶部坑 1)。
|
||||
- `scripts/check-content.mjs` 相应改判:原"列数不均衡""缺记录"两个用例换成"空列"(`/至少要有一条档案/`)与"条数过少"(`/至少需要每个分类一条档案/`),并新增两条正向用例——不等长列(1/12/5/2/9 = 29 条)与 1200 条(钉住 id 宽度补齐)。**20 项全绿**。
|
||||
- 全量方案带来的**盘内文件浏览、按路径收藏、列数参数化**能力随之失去,这是用户明确选择的取舍,不要再"顺手恢复"。要扩内容请往 `content/curated.mjs` 加条目(原因见顶部坑 4)。
|
||||
- **顺带修一个交互 bug**:刻度条窗口化在 8 条短列上把顺序整体转位成 `5,6,7,8,1,2,3,4`、高亮卡在正中(用户反馈"切换列左边的切换文件滑条坏掉了")。`main.ts` 的 `tickWindow()` 现在对 `len <= TICK_WINDOW` 走自然顺序分支,见顶部坑 2。
|
||||
- **新增右上角退出系统**(`#exit`,46px 圆形 `⏻`,与开场 `.skip` 同坐标切换):白屏 + logo(**不闪动**)→ 字符本体明灭(`shutdownFlicker`)→ 黑条从左扫到**完全覆盖字体**为止、**不向右滑出**(`shutdownSweep` 的 keyframes 只到 `inset(0 0 0 0)`)→ `SHUTDOWN_CLOSE = 880ms` 后 `window.close()`,关不掉时退化为纯白页。CSS 时序集中在 `style.css` 的 `.shutdown` 段,详见顶部「开场与退出的入口序列」。
|
||||
- ⚠️ **验证边界**:本机没有浏览器自动化、也没有无头 WebGL 环境,下面这套自检只能证明工程能构建、数据结构自洽,**不能证明动画观感**。退出序列、开场铺屏、刻度条观感这类结论必须由用户实机确认后才算通过。
|
||||
- **自检命令**(注意 `check:archive` / `check:loop` **没有** npm script,要直接用 node 跑文件;`check:content` 才是 npm test 形式):
|
||||
|
||||
```powershell
|
||||
npx tsc --noEmit # 类型
|
||||
npm run check:content # 档案数据校验(20 项)
|
||||
node --experimental-strip-types scripts/check-archive.mjs # 相机/抽取几何
|
||||
node --experimental-strip-types scripts/check-loop.mjs # 循环阵列
|
||||
npm run build # prebuild 会先跑归档快照 + 校验 + 字体
|
||||
```
|
||||
- `verification/ARCHIVE-ARRAY.md` 已标注为**存档**(描述的是被回退的方案),不要照它实现。
|
||||
- 2026-09-12:**归档重组 + 精选档案扩到 47 条 + 回归检查去掉写死条数**。用户指示"不要老是问我,你来下最优决策",于是把两件挂起的事一起做了。
|
||||
- **归档侧重组(18 项同盘 rename,零数据丢失)**:把纯货架内容上提到分类一级(`01_人工智能\01_训练产物\*`、`02_数据集\*`、`05_媒体\02_视频\*`、`03_截图\*`),并压平两类同名套娃——`07_日志与排查\下载日志\X.log\X.log`(5 份)、`ArmouryCrateInstallTool\ArmouryCrateInstaller_3.3.6.0\ArmouryCrateInstaller.exe`、以及 `03_代码与项目\{opencode-dev, awesome-design-md-main, CangjieCommunity-main, mc-bots-main}\<同名>\`。完整清单与回滚办法在 `E:\归档\00_索引\重组计划-*.json`,约定写在 `E:\归档\AGENTS.md` §3.3 与 §3 决策表。
|
||||
- **守恒已核对**(拿重组前的 `archive-index.json` totals 对比):文件 +2(两份计划文件)、目录 −15(11+4,全是搬空的)、字节 +5,207(计划文件 + 账目行),归档内容零丢失。
|
||||
- `content/curated.mjs` 里 X-001 / X-002 / X-003 的 `path` 随之上提,`department` 改为 `01_人工智能`;X-039 的摘要改成"五份日志"(原来描述的是目录套娃,已被压平)。
|
||||
- **精选档案 40 → 47**:补上此前**完全没有精选档案覆盖**的真实大件——NM2 原版权重(6.7 GB)、V2_dpskw 备份集(13.9 GB)、设计资料集、darwin 客户端、musicbase 曲库、录屏片段、`00_索引` 自身。编号接在 X-041…X-047,**既有编号一个没动**(校验器要求 `id === X-{index+1}`,所以只能追加)。五列现在是 **8 / 9 / 11 / 9 / 10**,这是**刻意不等长**,用来证明"每列不限"真的生效,不是数据错乱。
|
||||
- **`check-archive.mjs` / `check-loop.mjs` 里写死的 40 与 8 已拔掉**(它们正是坑 1 的翻版:改了条数就崩)。现在从数据推导:`documents` / `perColumn` / `maxPerColumn` 如实输出,只保留真正的契约断言(每列 ≥1、总数 ≥5、列内槽位不冲突;`slot` 复用在单列 >32 条时才允许)。`check-loop.mjs` 的 `files[wrap(row - 12, 8)]` 改成 `files.length`。
|
||||
- 全套自检:`tsc` 0 · `check:content` 20/20 · `check-archive` 0(47 条 / maxPerColumn 11)· `check-loop` 0(20000 次定向移动)· `build` 0(825 files / 33.9 MiB)。
|
||||
|
||||
## 正式功能提交与同步
|
||||
|
||||
- 用户授权:非实验性质的正式功能完成相关验证后,直接提交并合并到主分支,或提交后同步到对应远程分支,无需再次询问。
|
||||
- 当前已经位于主分支时,可直接提交并推送;在功能分支上则按项目分支状态完成合并与同步。
|
||||
- 提交应聚焦当前任务;同一工作区中尚在进行或等待用户确认的其他工作,保留原状,不混入正式功能提交。
|
||||
|
||||
## 解密与清晰内构
|
||||
|
||||
- 用户要求参考原片 41 秒正面与 46–51 秒多角度制作内部结构,并省略左侧三根高塔。模型继续通过 Blender MCP 生成,保留共享脚本与源文件。
|
||||
- 详情打开后由磨砂变清晰,解密线按原片 34–39 秒沿同一物理对角线合拢、保持、向中央收束,采用非线性运动。交互压缩时间,原片对照使用原时间码。
|
||||
- 360° 查看器可切换清晰/磨砂,切换不改变视角或拆解状态;返回详情保持解密,退回阵列恢复磨砂。
|
||||
- 长焦主场景近裁剪面为 5,以避免薄层内构深度冲突;保持原镜头和构图。验证见 verification/DECRYPTION.md。
|
||||
|
||||
## 内部模型的锐利截面(历史方案,已撤回)
|
||||
|
||||
- 用户要求内部环形模型更锐利,采用半透明磨砂质感,并打磨两环的连接处。内构沿圆周平滑,屋面、侧壁和切边之间保留硬法线;不再用多层圆角制造体积。
|
||||
- 两环通过一条切向接入、两端略展宽的连续薄片连接;内部仍完全容纳在盖板与基板之间。外壳几何与法线保持原样。
|
||||
- 源脚本为 art/internal_architecture.py;打磨前后与装配状态对照为 reference/internal-review.html,验证见 verification/INTERNAL-OPTICS.md。
|
||||
|
||||
## 外壳结构与磨砂揭示
|
||||
|
||||
- 用户要求黄色方块位于顶边并与外壳齐平,去掉左侧黄色竖条;螺丝仅保留右上与左下两处。
|
||||
- 外框刻线与螺丝周围的压槽全部置于盖板后。解密前整面磨砂,原片 38.84–39.56 秒由顶部向底部逐渐变清晰;上下半清晰差异只出现在移动过程,不能保留固定高度的清晰渐变。
|
||||
- 外壳细节通过 Blender MCP 制作,共用 art/shell_reference_details.py。新外壳保持既有内构顶点及法线,验证与对照见 verification/SHELL-REFINEMENT.md。
|
||||
|
||||
## 第一版内构恢复与轻磨砂
|
||||
|
||||
- 用户明确选择对照页左侧的第一版完整内构,来源为 89c7742 与 reference/internal-study/assembly-before.glb。恢复其两环截面、双连接带、平滑法线和原始白色/橙色材质;此前锐利截面、单连接片和追加内圈细节均已撤回。
|
||||
- 通过 Blender MCP 重新生成单体与拆解资产,保留 art/internal_architecture.py 和两个 .blend 源场景。已修订的外壳继续使用 art/shell_reference_details.py。
|
||||
- 选中盖板粗糙度为 0.42,并将折射模糊范围限制在投影盖板高度的约 1.6%,避免小尺寸预览过度模糊。自上而下揭示、阵列参数和完全清晰端点继续沿用。
|
||||
- 原始内构与当前内构对照见 reference/internal-review.html;验证见 verification/RING-TEXTURE.md。
|
||||
|
||||
- 用户查看恢复后的版本与磨砂修正后反馈‘效果很不错’,确认保留本版。
|
||||
|
||||
## 屏幕比例与触摸适配
|
||||
|
||||
- 用户授权按桌面不同比例、手机横屏、竖屏构图与触摸、实机性能校准的顺序完成适配;实机目标为 iPhone / Safari。继续采用本项目原生实现流程,不启用前端或动效 Skill。
|
||||
- 逐帧对照保留 1920×1080 舞台,完整等比展示;用户于 2026-09-10 追加要求正常登录入场铺满实际视口并移除字幕。交互阶段按实际视口布局。16:9 为视觉基准,桌面延展阵列,竖屏上方模型、下方独立滚动正文。
|
||||
- 镜头适配不能改变抽取高度、模型横向/纵深坐标、波浪、归位规则与基线材质。竖屏预览镜头也不能跟随卡片实时升降或轨道位置抵消运动。
|
||||
- 触摸在阵列区域左右切列、上下切档;详情获得净空后可单指检查,360° 查看器单指旋转、双指缩放和平移。正文滚动与场景手势隔离,旋转屏幕保留当前选档、视角与拆解状态。
|
||||
- 画质仍采用已有用户设置与原始默认值;不得以手机类型直接切到删减光影的性能档。实机校准必须区分真实 iPhone 数据、桌面 WebKit 兼容性检查与 Chromium 视口模拟。实现见 `src/viewport-layout.ts`、`src/responsive.css`,验证见 `verification/RESPONSIVE.md`。
|
||||
|
||||
## PWA 与线上入口
|
||||
|
||||
- 用户要求增加 PWA,以便 iPhone 从主屏幕进入独立窗口;沿用现有 Vercel 项目与正式域名 https://rhine.lubeiluchen.cc/,README 保留在线入口。
|
||||
- 正式构建缓存完整资源版本,更新下载失败时保留旧版。新版本准备好后由用户在设置中更新并重启,不中断当前浏览;保留收藏和偏好。
|
||||
- 开发模式不注册 Service Worker。图标复用项目共享品牌路径;主屏幕安装、离线与更新说明见 `docs/PWA.md`,验证见 `verification/RESPONSIVE.md`。
|
||||
- 针对 Win11 Chrome / Edge 的反馈,设置图标保留可见“设置”文字,新版本提供可直接点击的更新入口;`update.html` 从网络获取,用于旧版页面无法找到更新按钮时恢复,不清除收藏或偏好。
|
||||
- 动态效果默认遵循系统偏好,设置中明确说明跳过开机和简化选档的影响,并提供“启用完整动效并重播”。本站明确选择优先于系统默认,正文解密遮罩也遵循同一设置。验证见 `verification/PWA-RECOVERY.md`。
|
||||
|
||||
## 字体分包与开场启动(issue #6、#7)
|
||||
|
||||
- 用户于 2026-09-10 明确批准:比较 `misans` 与 `misans-webfont` 第三方分包,在确认加载收益和显示效果后替换官方整包。本项目允许采用第三方 MiSans 分包,此明确授权覆盖此前禁止第三方分包的全局默认;保留字体署名、许可、来源和固定版本记录。
|
||||
- 分包随项目同源部署,按页面字符加载;保持现有 MiSans 的字形、字重映射与排版。PWA 仍缓存完整版本,避免新档案或离线检索缺字。
|
||||
- 比较后采用 `[email protected]`,字体 4.003;固定版本与源哈希保存在 public/fonts。品牌三行宽度按基线重新校准,首次入口字体约 415 KiB。选择依据、完整离线体积取舍与验证见 `verification/STARTUP-LOADING.md`。
|
||||
- 开场等待用户点击、轻触或键盘启动,解锁声音并完成准备后统一开始音画;保留静音和减少动态效果偏好,加载失败提供重试与无声进入。
|
||||
- 用户授权完成验证、提交及同步后,回复 GitHub issue #6、#7 并关闭它们。
|
||||
|
||||
## 桌面工作台(实验)
|
||||
|
||||
- 用户于 2026-09-10 批准在同一壁纸中切换档案展示与实用工作台,首版为时间日期、三条今日事项、重要日程倒计时、系统媒体信息与专注计时。沿用既有原生实现、视觉和 Blender 资产。
|
||||
- 模式与事项内容、日期、计时时长从 Wallpaper Engine 属性读取。事项完成状态每日重置,专注计时按绝对截止时间恢复;这些进度独立存储,不受宿主声音与画质覆盖。
|
||||
- 用户提出把玻璃档案交互用于真实文件夹。本次仅评估可行性,尚未授权安装本地桥接服务;研究见 docs/DESKTOP-FOLDERS.md。
|
||||
- 用户于 2026-09-10 要求工作台严格沿用原主页设计,撤回首版绿色强调色与有底色的面板。采用原主页近黑/暖灰、细线与竖线刻度,规范见 DESIGN.md。
|
||||
|
||||
## 工作台显示内容与纯净效果
|
||||
|
||||
- 用户于 2026-09-10 决定将纯净效果与工作台合并,通过按需隐藏元素配置画面,不另建纯净模式。
|
||||
- 七项显示开关全部位于 Wallpaper Engine 壁纸属性:时钟日期、今日事项、右侧功能内容、底部功能导航、品牌文字、页脚信息、设置入口。默认全部显示,仅影响工作台交互阶段;开场与档案展示沿用原样。
|
||||
- 用户明确允许隐藏设置入口,恢复通过 WE 属性完成。壁纸内不提供另一套临时显示开关。隐藏内容不清除任务状态或停止专注计时。
|
||||
- 音乐频谱、自动演出和小游戏仍为讨论中的后续方向,本次显示配置不代表已实现这些效果。
|
||||
|
||||
## 明暗配色(壁纸实验)
|
||||
|
||||
- 设置及 Wallpaper Engine「入场与画面」分组提供亮色/暗色,默认亮色。暗色采用石墨背景、烟灰玻璃、暖白文字与琥珀标记,沿用原生实现。
|
||||
- 切换时以当前档案为起点,沿行列依次改变卡片材质;背景与文字同步渐变。中途反向从当前颜色接续,减少动态效果时直接到位。
|
||||
- 详情解密后盖板恢复清透,内部结构保持可读;查看器、工作台与开场共同使用配色。实现与验证见 verification/THEME.md。
|
||||
|
||||
- 用户要求静止呼吸可独立开关,且「始终归齐」也可生效。WE 工作台显示分组新增该开关,默认开启;音乐时淡出、静音后恢复。用户认为现有音乐律动杂乱,已提出鼓点主导宽波的后续方案,当前未替换音乐算法。
|
||||
|
||||
- 用户批准原版、鼓点宽波和整组轻抬三种音乐律动并列试用;WE 属性中选择,默认保留原版,尚未决定最终方案。原有呼吸开关继续独立生效。
|
||||
|
||||
## 自适应阵列与暗色雾气
|
||||
|
||||
- 用户要求解决边角露出阵列尽头,并将屏幕外档案裁剪、按屏幕覆盖范围动态决定数量作为同一任务。
|
||||
- 交互阵列按当前实际镜头与视口计算候选范围,逐实例裁剪并紧凑提交;替代固定 9 列 × 32 行的交互窗口。显式原片对照继续保持原来的 160 个位置。
|
||||
- WE「入场与画面」提供自动标准余量/自动额外余量,默认标准。余量包括边缘预留、卡片完整高度和附近阴影贡献;相同宽高比与构图不因像素数量增加而复制更多档案。
|
||||
- 暗色采用可见灰雾,并加强顶部、底部和边缘淡出。连续循环、点击、抽取、归位与原片对照回归见 verification/ARRAY-COVERAGE.md。
|
||||
|
||||
## 工作台衔接与连续频谱修订
|
||||
|
||||
- 用户要求删除媒体无进度与本机时区说明,修复暗色刻度、重播悬停和接力退出与设置的重叠。工作台开场隐藏选档标签,Powered 淡出;工作台与档案展示均保留退场过渡。
|
||||
- 用户比较后保留原版律动为默认,撤回鼓点 A 与整组轻抬 B。重做 A 为按屏幕从左低频、中间中频到右高频的持续频谱,B 为持续层叠流波,两者均响应持续音乐。新 A/B 待试用。
|
||||
- 正常开场同样动态覆盖实际视口,只有显式逐帧对照保留 160 个档案位置。实现与复核见 verification/WORKBENCH-REVISION.md。
|
||||
|
||||
- 媒体信息各通道独立更新:不能在歌曲文字回调中删除封面或进度。保留先封面后文字、同封面切曲与播放器切换;只有相应通道的显式空值替换旧状态。见 verification/WORKBENCH-REVISION.md。
|
||||
|
||||
- 用户进一步要求游戏模式进出也有过渡:工作台淡出、游戏信息进入,退出反向衔接;快速打断延续当前状态,退场期间保持输入隔离,完成后恢复焦点,减少动态效果直接切换。
|
||||
|
||||
## UI 视差与画面质感(壁纸实验)
|
||||
|
||||
用户明确要求仅 UI 产生 HUD 曲面视差,壁纸三维镜头不随被动鼠标移动。按参考项目的镜头畸变思路,UI 各区域以共同径向曲面投影到不同梯形;独立「鼠标追踪」关闭后保留静态形状。WE「HUD 与画面质感」提供曲面纵深、全局屏幕效果、局部界面磨砂底,三项默认关闭,强度可调。
|
||||
|
||||
用户追加明确:色散、暗角与颗粒处理最终全局合成,覆盖 UI、三维画面和弹窗,替代此前仅三维画布的 ShaderPass。颗粒增加尺寸控制;减少动态效果时冻结颗粒并停止鼠标追踪,静态曲面仍可保留。开场无 UI 视差,全局屏幕效果同样可见。磨砂底随 UI 投影。继续原生实现,见 verification/VISUAL-EFFECTS.md。
|
||||
|
||||
用户追加要求 UI 上、下、左、右四边距分别使用滑杆,并纠正为允许负数。WE「界面边距」提供四个 −300~300px 的独立偏移,默认 0;正值内收,负值外扩,抵消舞台缩放。仅调整交互 UI 布局,三维画布保持铺满;支持曲面与磨砂。实现与验证见 verification/UI-INSETS.md。
|
||||
|
||||
|
||||
2026-09-10 修订:曲面纵深、细颗粒、颗粒尺寸、边缘色散、暗角的默认数值统一为 20。下边距不再缩短桌面工作台的中部定位容器;底部导航和页脚先上移,实际接近中部内容时才推动对应内容,上方品牌限制继续上移,空间不足时内容独立滚动。手机窄屏继续采用原有纵向滚动布局。
|
||||
|
||||
WE 新增「工作台 · 可用功能」,时间日期、今日事项、重要日程、正在播放、专注计时各自可开关,默认全开。关闭功能同时移除其右侧内容入口和底部按钮;当前项关闭时选择首个仍开启功能,全关时隐藏右侧内容和导航。左侧时钟与事项仍由原显示开关控制,任务和计时状态保留。用户明确要求自行验证,本轮仅完成实现和构建打包,未进行运行时与视觉验收。
|
||||
|
||||
|
||||
2026-09-10 后续修正(覆盖上述中部避让方案):用户明确下边距只移动底部两排,排间距固定,中部组件不压缩、不滚动。已删除 workbench-insets 自动避让;工作台高度不再随下边距变化。底部导航、游戏入口与页脚在曲面投影之后使用相同 translate 偏移。另修复 :is 内 ID 提高优先级导致原规则覆盖,以及减少动态效果的隐式 all 过渡污染曲面测量的问题。Edge 实际页面完成 1920×1080、2560×1440、1600×900,曲面 0/20/80,下边距 60/192/300/−80/0,共 45 组检查:中部坐标及尺寸固定,无新增 overflow,底部各行位移一致。画面对照与数据见 verification/ui-insets/。
|
||||
|
||||
WE「入场与画面」新增暗色专用开关「暗色模式仅选中档案标签使用强调色」,默认关闭保留现有选择。开启后阵列与归位档案的顶边标签使用暗色,当前选中档案保留琥珀强调;浅色不受影响。通过既有材质 uniform 实现,无新增网格与绘制通道,暗色开/关及浅色截图已检查。
|
||||
|
||||
|
||||
## 超级性能模式与自定义画质
|
||||
|
||||
用户于 2026-09-10 要求低配壁纸模式与减少动态效果独立,并恢复 WE 原生自定义画质。新增「超级性能模式(保留动态效果)」开关,默认关闭;开启时以独立的有效画质覆盖已保存画质,不改减少动态效果、宿主帧率或音乐律动设置,关闭恢复当前预设/自定义配置。
|
||||
|
||||
超级模式:三维比例 60%、DPR 上限 1、总像素不超过 921600;关闭阵列阴影、AO、景深和 SMAA,直接输出主场景;背景阵列使用无折射、无清漆材质并隐藏小螺丝,选中/详情模型保留完整结构和低分辨率折射。全局屏幕滤镜暂时停用,文字磨砂改为无模糊底色,HUD 曲面与追踪继续运行。DOM 文字不降低分辨率。
|
||||
|
||||
WE 画质下拉增加「自定义」,仅此选择且超级模式关闭时显示九项控制:渲染比例、像素密度、抗锯齿、阴影、AO 采样、AO 分辨率、景深、透明材质分辨率、纹理过滤。连续参数用滑杆,离散参数用原生下拉。部分宿主回调保留其他参数;超级模式开启期间保留自定义更新,关闭恢复最新配置。完整证据见 verification/SUPER-PERFORMANCE.md。
|
||||
|
||||
## 三维资源开关与自定义壁纸
|
||||
|
||||
- 用户于 2026-09-10 授权在左下 SESSION 右侧增加 3D 开关:档案错峰下落退场后实际释放全部三维场景、查看器和 WebGL 资源;再次开启重新加载并从下方逆向升起,保留选档。关闭途中可反向恢复,减少动态效果时直接切换。
|
||||
- 3D 退场、关闭与载入期间隐藏游戏入口及模型专用入口;重新开启按原显示偏好恢复。2D 工作台和正文继续可用。
|
||||
- WE「入场与画面」增加可选自定义壁纸,默认不启用;用户选图后,仅在三维资源释放后淡入。重新载入成功时图片淡出,失败保持图片并允许重试。沿用原生实现,见 verification/THREE-RELEASE.md。
|
||||
- 用户明确保留三种音乐律动并列选择:原版、连续频谱、层叠流波。原版仍为默认,不再标为待定 A/B。
|
||||
- 修复壁纸自身交互声音触发音乐归齐:静音状态抑制本地音效引起的新频谱活动,已建立的音乐继续响应。全部归齐时取消标签强调,暗色仅选中强调随实际升降连续过渡,见 verification/SELECTION-STATE.md。
|
||||
|
||||
## 创意工坊发布准备
|
||||
|
||||
- 用户于 2026-09-10 曾授权发布,随后明确改为自行操作发布,代理负责 GIF 和操作说明;当前尚未上传。
|
||||
- 正式名称为 Rhine Lab · 莱茵生命交互桌面,去掉“本地测试”。独立正式工程放在 Wallpaper Engine 的 myprojects/rhine-lab-workshop,保留之前本地试用工程。
|
||||
- 预览参照用户指定的 3088099655 预览的全景/局部剪辑方法,素材全部来自本工程工作台实际运行画面。最终 GIF 256×256、13.2 秒,含时钟事项、专注与明暗切换;没有将参考壁纸的图片放入发行包。
|
||||
- 发布操作和后续更新须保留 workshopid,见 docs/WORKSHOP-PUBLISH.md;预览来源与验证见 verification/WORKSHOP-PREVIEW.md。
|
||||
|
||||
- 用户要求增加「启动时加载 3D」WE 属性,默认开启;关闭时只播放前段 2D 入场并进入组件页,不创建三维场景,左下可手动加载。属性下次加载生效。自定义图片在 2D 开场后显示,支持 WE 编码盘符和失败后同路径重试。见 docs/WALLPAPER-ENGINE.md。
|
||||
|
||||
- 用户要求自定义壁纸上下遮罩范围可调:WE 图片设置下新增 0–100 滑杆,默认 100,0 完全关闭遮罩;仅自定义图片显示时生效,沿用原生实现。见 docs/WALLPAPER-ENGINE.md。
|
||||
|
||||
- 用户要求改善无遮罩图片背景下浅色页脚按钮的对比,并将既有滚动库应用到时钟、播放器时间及切歌文字。使用原生实现与现有 createRollingText,460ms direct,媒体原位更新,减少动态效果直接显示。见 docs/WALLPAPER-ENGINE.md。
|
||||
|
||||
- 用户撤回额外白底的可读性方案:删除页脚新增底色与外扩阴影,仅调整底部导航、待办、提示及按钮的文字颜色;原玻璃背景和模糊参数保留。
|
||||
|
||||
## 壁纸功能回合主分支(2026-09-11)
|
||||
|
||||
用户明确授权评估兼容性后更新 main,覆盖此前不合并主分支的实验限制。共用暗色主题、自适应阵列、开场铺屏、滚动数字修复与资源管理进入网页;超级性能模式适配为网页持久化设置,默认关闭,独立于减少动态效果。工作台、系统音频/媒体、HUD 属性、自定义图片与 3D 卸载入口仍仅在 wallpaper 构建启用。普通网页保留启动声音解锁、键盘与触摸、PWA 和自定义画质。继续沿用原生实现流程。验证见 verification/WEB-INTEGRATION.md。
|
||||
|
||||
- 2026-09-11:用户要求开启 HUD 时开场同样应用曲面与鼠标追踪,覆盖此前开场无 UI 视差的限制。Logo、验证文字、圆环和欢迎画面按当帧原始动作叠加投影,品牌与 Powered 共用同一曲面;切换阶段保留深度与指针跟随状态。磨砂底仍仅交互阶段启用。见 verification/BOOT-HUD.md。
|
||||
@@ -0,0 +1,306 @@
|
||||
# 视觉与行为基准
|
||||
|
||||
## 2026-09-09 · 屏幕比例与触摸适配
|
||||
|
||||
用户授权执行桌面比例 → 手机横屏 → 竖屏与触摸 → iPhone / Safari 实机校准。沿用已确认的模型、字体、色彩、光照与原生实现方法。
|
||||
|
||||
- `viewport-layout.ts` 统一舞台尺寸与取景参数。启动/重播保留完整 16:9 参考舞台;桌面交互以 1080 逻辑高度延展宽度;窄屏和竖屏使用真实 CSS 像素布局,文字与按钮不再整体缩小。16:9 基准仍落在原坐标。
|
||||
- 横屏保持左侧模型、右侧档案。窄横屏正文独立滚动,导航适当错行;竖屏正文从屏高 54% 开始,保留上方模型、返回与 360° 入口。短屏的取景范围额外受返回按钮与正文上沿约束。
|
||||
- 模型位置与运动函数继续沿用原值。通过相机锚点和垂直取景范围保证构图,竖屏预览使用固定基准焦点,避免抵消抬起、呼吸和轨道运动。开场取消留白时,镜头先维持模型的实际显示尺寸,再通过既有相机插值接入交互构图。
|
||||
- 品牌使用原版 MiSans 排版和同一标志,小屏缩放整组品牌;正文保持 15px,检索输入 16px。主要操作至少 44px 触控高度,档案刻度为次要跳转目标。顶部/底部与两侧边距读取安全区域。
|
||||
- 左滑进入下一列,上滑进入下一档案,反向手势对应上一项。滑动需超过 36 CSS px、主方向至少为次方向的 1.3 倍,且在 1.4 秒内完成;轻点继续用射线选择,多指或取消事件不触发切档。详情的模型拖动使用实际坐标差,保留净空与转正限制。
|
||||
- 独立查看器单指旋转、双指缩放和平移。竖屏为模型保留独立画布,装配结构移至下方三列;拆解时取景轻微展开,缩放和平移仍由用户控制。旋转屏幕只调整投影与布局,不复位视角、内容或拆解进度。
|
||||
- 软键盘只改变弹窗可用高度,检索、收藏、设置均可滚动;详情排版变化后重新测量解密遮盖与页签下划线。页面隐藏和二维片头完全覆盖时停止三维绘制,画质、透射和光照默认值不变。
|
||||
- 校准入口 `reference/mobile-review.html` 通过 `npm run review:mobile` 启动;只在开发服务器接收有限大小的帧时报告,保存至 `.tools/mobile-calibration`。该入口不进入生产构建,也不将桌面 WebKit 跑分视为 iPhone 成绩。
|
||||
|
||||
验证与实机状态见 `verification/RESPONSIVE.md`。
|
||||
|
||||
## 2026-09-09 · 档案标题滚动
|
||||
|
||||
按用户新要求,主档案标题采用与数字一致的滚动效果,替代历史快切黑条。复用已安装 `@kitlangton/rolling-number` 的 `createRollingText`,与编号共享 460ms 运动配置;使用 direct 模式直接替换中文、关闭逐字延迟,字数变化交由相同的宽度弹簧处理。固定向上滚动,标题模糊强度为 0.2;字号、颜色与位置沿用原样。保留单份可访问与可选择的语义文字,装饰字槽不进入复制内容。
|
||||
|
||||
连续输入由现有引擎从当前运动接续,只显示可见过渡与最新目标,不排队播放每个中间标题。进入详情、重播或开启减少动态效果时立即落到最终文字。历史黑条实现已移除;规范与回归检查见 `verification/ROLLING-TITLE.md`。
|
||||
|
||||
## 2026-09-09 · 详情与弹窗过渡补全
|
||||
|
||||
合并 `codex/boot-motion-polish` 的查看器平移、缩放与 560ms 球面复位,沿用该分支的品牌文字校准。详情层以 180ms 进出,正文仍跟随实际镜头进度显示;返回时不再立即删除可见内容。阵列氛围与右侧浅色遮罩按同一镜头进度交叉渐变,保留两端既有画面。快速反向从当前透明度接续,进入正文的焦点等待可读状态。
|
||||
|
||||
检索、收藏和设置弹窗统一使用 300ms 入场、200ms 退出。面板入场位移 12px、退出位移 8px;进入中途关闭时从实际透明度及位移继续。退出前保留对话框节点、焦点和背景输入隔离,重复 Esc 不向底层传递;选档与重播在关闭完成后执行。
|
||||
|
||||
页签正文采用 150ms 淡入,下划线使用 180ms 连续移动;快速切换取消旧内容动画并显示最终页签。收藏仅修改按钮、收藏计数及 220ms 颜色反馈,保留阅读区域节点、滚动位置与按钮焦点。新增过渡遵守减少动态效果设置。实现见 `src/ui-transitions.ts`,验证见 `verification/UI-TRANSITIONS.md`。
|
||||
|
||||
## 参考与既有规范
|
||||
|
||||
参考为目录原片的 5–40 秒,1920×1080、25fps。用户选择参考驱动的原生实现,不使用前端或动效 Skill。美术模型通过 Blender MCP 制作,界面使用 TypeScript、Three.js、Vite。
|
||||
|
||||
- 暖灰白底、黑色文字、暖杏金选中信号。界面以细线和紧凑排字组织,保留原片标题与右下角署名位置。
|
||||
- 字体为用户指定的 MiSans。2026-09-10 用户明确批准为加快加载改用第三方分包;比较后采用 `[email protected]`(源字体 4.003),固定版本同源部署,按字符加载。保留 300、400、600、700 四个字重映射与 `font-display: swap`;后备为 PingFang SC / Microsoft YaHei / system-ui。字体分片字节不改动,移除上游 CSS 的 local() 以免设备字体覆盖固定版本。版权、许可和分片哈希在 public/fonts,设置页含署名与许可入口。详见 `verification/STARTUP-LOADING.md`。
|
||||
- 5–27 秒为接入、身份验证、授权扫描、欢迎和白场。26.92–27.67 秒,阵列从左侧滑入,镜头从接近侧视快速抬高,随后进入斜俯视扫描。
|
||||
- 27–29 秒为第一轮传播;29–30.5 秒反向回传。30.58 秒开始第一段小幅抽取,之后有停留;32.55 秒开始第二段抽取,镜头随后继续转向和推近至 39 秒。
|
||||
- 选中卡片两侧在回传后继续起伏。主通道保留较高的连续肩部,其余通道幅度较低;按距选中位置的距离延迟并衰减。
|
||||
- 相机校准必须使用未被遮挡的顶部边线、标签和重复卡片端点。不能把前排遮挡后的可见高度当作整张卡片高度,也不能通过扩大通道间距或设置通道高度阶梯补偿可见列数。
|
||||
- 当前 31.48 秒镜头约为方位角 59°、仰角 19°、垂直取景范围 7.33 世界单位。远距镜头配合小视场角压缩透视。相邻通道同高、中心距 5.2;卡片宽 5、高 3.7,行距 0.62。后半段镜头转向与抽取升降分别控制。
|
||||
- 39 秒特写由屏幕锚点控制构图,镜头逐渐转至约 18° 方位角、13.8° 仰角。保密等级扫描线投影在同一个实时模型上。
|
||||
- 模型由磨砂盖板、基板、内部光学腔、环槽、边框、螺钉、标签与刻线组成;圆环位于盖板后方。重复卡片也启用物理透射,使用简化内部基板与象牙色端面。
|
||||
- 交互保留检索、切换、详情、收藏、导出和设置。轨道与升降采用保留速度的临界阻尼;旧卡片独立归位,快速重新选中时接续运动。
|
||||
- reference/review.html 仅在开发环境对照原片与实时场景;暂停使用原片逐帧图,播放使用视频。产品入口和生产构建不嵌入原片。
|
||||
|
||||
实时屏幕空间透射无法递归恢复原片全部透明层间折射。当前实现以关键帧投影和连续播放复核,尚未通过逐像素差分认证。
|
||||
|
||||
## 用户标注后的镜头与几何修订
|
||||
|
||||
- 三张标注经去除黄色笔迹区域后与原片帧匹配,对应 27.60、29.16、31.56 秒。
|
||||
- 档案端面深度约 0.31、行距 0.62;原来的端面偏薄。由 Blender MCP 沿模型厚度方向调整并重新导出,标签与投影标记同时贴合新表面。
|
||||
- 前半段采用较近的透视镜头:27.60 秒约 28 世界单位镜头距离、43° 仰角;29.16 秒约 35 世界单位距离、35° 仰角、67° 方位角。之后连续转至既有选中构图,特写镜头距离为 72。
|
||||
- 回传波进入时机向后校正,29.16 秒尚未形成明显右侧回波,与标注帧对应。
|
||||
- 删除所选模型朝 z=3.1 的位置插值,以及背景阵列随 detail 额外下沉的逻辑。选中槽位在抽取全过程固定横向与纵深位置,模型只有高度变化,镜头负责构图。
|
||||
|
||||
## 交互预览与进入详情
|
||||
|
||||
- 左右切换停在原片 31.56 秒对应的浅抬起状态(0.4 世界单位),邻排沿同一肩部轮廓排列;点击 ACCESS FILE 或按 Enter 后继续升至 3.35。返回恢复预览,详情中换选也先回到预览。
|
||||
- 卡片超过浅抬起高度后,镜头才逐渐转向并把卡片放在画面左侧;详情文字随镜头进度在右侧渐显。抽取不再自动旋转模型,详情拖拽仍可检查角度。
|
||||
- 阵列与抽出模型共用外壳几何,在同一个表面上随升降连续插值颜色、粗糙度与透光率;内部结构以连续覆盖率显现,文字标签渐显。每张归位卡片拥有独立材质状态,接近完全归位且外观一致后才恢复阵列实例。
|
||||
|
||||
## 最新交互修订(覆盖上文旧的按键和详情高度)
|
||||
|
||||
- 左右键切列,上下键切换列内前后档案。五列分别为工程研究、生命科学、机构档案、能量研究、特别项目,各 8 份,共 40 份;切回一列保留上次的档案选择。
|
||||
- 760 帧(30.40 秒)各列波峰等高,随后摄像机右移,把同一物理列置于画面中央;785 帧附近非选中列仍保留 25% 以上的肩部波幅。列间突出程度采用连续插值。
|
||||
- 交互详情升高 4.05 世界单位;预览仍为 0.4,原片时间轴的完整升高仍为 3.35。删除试调的 5.0 高度,保持邻片可见。
|
||||
- 旋转限幅为 ±0.8 弧度。只有卡片完全避开邻片后才接受拖拽;返回先在原高度转正,再下降,镜头在转正期间维持构图。附近旧卡片转正时,新预览暂缓抬起,避免相互穿插。
|
||||
- 标签和启动界面共用 src/brand.ts 的标志路径,标签不再手绘近似曲线。
|
||||
- 档案正文为基于公开世界观资料的原创编目文章,不冒充游戏内原始档案。每份包含设定参考链接,详见 reference/LORE-SOURCES.md。
|
||||
- 详情切换时,雾效距离跟随阻尼之后的实际镜头位置,避免进入时相机尚未到位、卡片提前被远雾覆盖。保留既有镜头轨迹与最终雾效。
|
||||
- 交互预览停止操作 2.5 秒后渐入呼吸起伏:8 秒与 13 秒周期叠加,最大单向位移 0.102 世界单位(约卡片高度的 2.8%),相邻卡片略微错相。根据用户反馈,幅度由最初版本增至 3 倍,保留缓慢节奏。切换档案、进入详情或开启减少动态效果时渐退;原片时间轴不叠加此效果。
|
||||
- 琥珀色侧条与透明盖板只有 0.002 世界单位的前后间距,斜视时会出现深度冲突条纹。仅为 Amber_Lightguide 材质增加 polygonOffset(factor=-1,units=-2),克隆归位卡片同时继承。保留原有相机裁剪范围、模型几何、材质色彩与全局 AO 参数。
|
||||
|
||||
## 独立模型查看器
|
||||
|
||||
- 详情页的「360° 查看文档模型」打开独立场景。沿用暖灰背景、既有字体、细线与克制的中英文字阶;不引入新的前端或动效设计方法。
|
||||
- 鼠标左键拖动可完整环绕模型;上下左右键平移视角,滚轮或加减键缩放。Home 或「复位视角」恢复初始取景;ESC 或「返回档案」恢复原档案与键盘焦点。
|
||||
- 「拆解档案」将紧固件、透明盖板、折射环组、光学核心、信息基板、背板与框架六组部件沿厚度轴分离。「一键重组」使各部件准确回到原位;途中反向操作保留当前运动位置与速度。减少动态效果设置下直接切换。
|
||||
- 独立场景暂停主场景绘制,并复用校准后的材质、环境光配置与当前档案标签。原阵列模型与抽取镜头不受查看器变换影响。查看器的近裁剪面为 0.3,远裁剪面为 120,初始相机位置为 (7.2, 3.8, 12),可缩放距离为 5–28。
|
||||
- 部件资源由 Blender MCP 执行 art/build_assembly.py 生成:复用原建模脚本的几何,按物理部件和材质合并,导出带 assemblyPart 信息的 public/assets/archive-assembly.glb,并保留 art/archive-assembly.blend。
|
||||
- 复现时通过 Blender MCP 在 Blender 内执行该脚本,并在执行作用域中传入脚本的绝对 __file__ 路径;脚本以自身位置定位项目。几何双向顶点距离检查允许 1e-5 的浮点误差,实测最大误差约 6.08e-7。
|
||||
- 根据用户的内外光照对照,查看器改为在自己的 WebGL 上下文内生成相同的 RoomEnvironment/PMREM。禁止跨 renderer 直接复用 GPU 渲染目标纹理,否则环境光缺失会使内部基板和圆环发暗。背景统一为 #eae5e1,复用主界面的氛围渐变,雾效范围相对实际模型距离设为 -1 至 +12,使放大缩小时保留浅色磨砂质感。
|
||||
|
||||
## 循环档案阵列
|
||||
|
||||
- 主界面将内容编号与物理位置分开:五类档案和每列八份档案按周期映射到连续的行列坐标。上下、左右的方向操作每次改变一个物理位置;最后一项接续第一项时,轨道继续沿原方向运动。
|
||||
- 切换列时保留该列的档案选择记忆,并选择附近的对应周期位置;点击场景卡片使用当前实例实际对应的档案与位置。
|
||||
- 交互时维护 9 列 × 32 行、共 288 个位置,按镜头附近的可见窗口循环补位。主模型与归位副本按物理坐标独立管理,避免同一档案的不同周期副本互相覆盖。
|
||||
- 横向采用整组阵列轨道平移,纵向保留原有前后轨道;两者都保留阻尼与速度。取景和灯光附近的实际坐标保持有限,选定后的抽取仍只改变高度。
|
||||
- 当逻辑坐标超过 2048 时,按内容周期统一平移坐标与运动状态;保持世界位置、速度、波纹与呼吸相位连续,避免长期运行后浮点精度下降。
|
||||
- 显式原片逐帧对照保留原始的前 160 个位置;正常开场与交互阶段均按实际视口动态生成背景档案。循环后的重播会清理归位副本。
|
||||
|
||||
## 切换波浪微调(历史实验)
|
||||
|
||||
- 以循环阵列提交 aeddd8f 为基线。用户试用减弱波浪后,明确要求恢复原来的波浪幅度与节奏,仅去掉负向波谷。
|
||||
- 交互选中波浪保留 0.8 幅度系数、0.2 秒起势、原衰减率和传播速度,仅把余弦的负值截为零。正向波峰仍会升起并回落,波浪本身不再将档案压低到基础肩部轮廓以下。
|
||||
- 验证入口为 reference/rise-review.html;原片时间轴、浅抬起高度、详情抽取与归位规则沿用当前基线。
|
||||
- 用户请求的可视对比页为 reference/wave-compare.html:两份实际 ArchiveScene 共用资源与时间步长。左侧可选择原始基线 aeddd8f 或仅去除负向波谷的 2048536,两者均为同时播放;右侧保留先抬起后波浪的实验方案。支持单步、连续、反向切换,0.25×/0.5×/1× 播放、逐帧定位、选中区域放大与高度曲线。每次回放检查实际位置与预先采样的轨迹一致。该页仅用于开发环境审阅。
|
||||
|
||||
## 先抬起,再波动(历史实验,当前不采用)
|
||||
|
||||
- 该实验的新选中档案先抬起,再发出选中波浪。预览目标仍为 0.4,向上抬起的临界阻尼速率从 4.2 调为 7.6;高度达到 0.35,且同列前后四格内的归位旧档案比它低至少 0.015 后,才启动波浪。单次正常切换约 0.48 秒后启动;旋转归位等特殊情况按实际状态等待。
|
||||
- 每次选择替换尚未发出的波浪。进入详情、重播时取消待发波浪;减少动态效果时不发波浪。原片时间轴继续采用原值。
|
||||
- 用户进一步要求消除选中档案再次跳起的效果:涟漪在源点的位移为零,并在周围 2.5 行的距离内平滑展开;正余弦包络改为平方,使边缘以零速度起落,保留远处波峰的幅度和传播速度。选中档案仅完成原定的 0.4 抬起,自己的涟漪不再给它叠加高度。
|
||||
- reference/sequence-review.html 验证先后关系、归位高度、连续快速切换、反向、详情旋转归位、减少动态效果与重播取消。
|
||||
- 档案序号与列编号采用 @kitlangton/rolling-number 0.4.1 的原生 DOM 接口,460ms 滚动,固定两位数,低强度运动模糊。方向键跨循环边界时保留滚动方向,快速输入由组件延续当前运动状态;系统减少动态效果与页面设置均可关闭滚动。MIT 许可随包保存到 public/licenses/rolling-number.txt。
|
||||
|
||||
## 当前采用:原始基线动效与光影微调
|
||||
|
||||
- 用户对比后明确回到原始基线 aeddd8f。生产场景默认使用完整的有正负波谷的选中波浪,选中与波浪同时开始,升降阻尼速率恢复 4.2。此前实验保留在参考页面;Rolling Number 沿用。
|
||||
- 灯光统一到 src/archive-lighting.ts:曝光 1.00,环境强度 0.52,暖白主光 1.7,主光位置 (-8,14,4),补光降至 0.3,半球光降至 0.5。主场景和独立查看器各自在自己的 WebGL 上下文生成环境贴图。
|
||||
- 阵列盖板增加少量暖色吸收,斜面和背板偏向浅香槟色;内部圆环与基板保持可读的暖灰层次。吸收色和吸收距离与其他材质属性一起随升降过渡。阴影偏移小幅收紧,AO 半径从 0.38 调为 0.44。
|
||||
- 模型、几何、布局与镜头沿用已确认版本。reference/light-review.html 可切换四个关键帧以及原视频/调整前参照;两侧实时场景使用相同模型位置与镜头,并自动检查一致性。原片关键帧需在本地 reference/motion 中准备,详见 verification/LIGHTING-POLISH.md。
|
||||
|
||||
## 当前采用:基线配色与档案编号滚动
|
||||
|
||||
用户对比后确认整体基线版本的颜色更合适。生产场景和独立查看器默认切回 baseline,包括材质颜色、透光参数、曝光、灯光与阴影/AO;refined 仅保留在光影对照页中作为实验。
|
||||
|
||||
X-001 形式的主档案编号和悬停编号复用现有 Rolling Number:固定 X- 前缀,三位数字保留前导零,460ms 滚动并支持快速输入衔接。方向键切换沿导航方向滚动;悬停首次出现直接显示,再切换目标时滚动;减少动态效果时立即更新。编号的字号和布局保持原样。
|
||||
|
||||
## 标题快切与 360° 查看器进出
|
||||
|
||||
历史标题方案(已由 2026-09-09 的滚动文字替换):主档案标题在连续变化间隔小于 240ms 时触发短促闪动,140ms 后变成稳定的黑色横条;横条锁定本轮开始时的宽度。停止 260ms 后以 180ms 短闪显示最终标题,单次切换直接显示。切入详情、启动或减少动态效果时清理计时和动画。
|
||||
|
||||
360° 查看器以 320ms 淡入覆盖原详情,页眉和页脚轻微上移就位;模型完成载入并渲染后以 380ms 从 0.97 倍展开并淡入。退出以 220ms 淡出和轻微收拢接回原档案。使用可取消的原生动画,进入途中按 Esc 从当前透明度退出;退出结束之前继续绘制、保持模态输入隔离,之后才释放模型、恢复详情和原按钮焦点。过期的异步载入结果按请求序号释放。减少动态效果时直接切换。
|
||||
|
||||
reference/micro-transition-review.html 验证真实应用中的快切、最终标题恢复、切入详情取消横条、进入打断、重复关闭、拆解中退出、焦点归还和重新载入。2026-09-08 实测 13 项检查全部通过。标题短时动画通过定位到动画末端检查,避免后台标签页对计时器的节流导致误判。
|
||||
|
||||
## 2026-09-08 · 启动前段逐帧修订
|
||||
|
||||
以原片 25 fps 连续帧复核 6.76–27.00 秒。用户排除黑底鹰角片头,启动和重播从画面变白后开始。应用的内部参考时间仍为原片时间减 5 秒;首次加载遮罩退出后才开始显示英文输入。
|
||||
|
||||
`boot-motion.ts` 将逐字输入、停顿、清空、闪切固定到原片帧点,空间位置使用连续插值。`boot.ts` 驱动 DOM/SVG,不依赖循环 CSS 动画的已播放时长,因此正常播放、慢放和前后跳帧使用同一画面状态。英文从固定左端输入,标志从右上弧线开始绘制、绕左环再补全右环,完成后向左移位;品牌三行错开出现。
|
||||
|
||||
身份段恢复原文 `ID CONFIRMED : JOYCE MOORE`、`REQUEST RECEIVED`、`START PROCESSING...`。权限段用从画面外收拢的黑白双环、反向内弧、橙色节点、四帧缩放跳变和中心图形切换。欢迎段保留黑白闪切、公司名称黑底横向扫过、第三行 `INTERNAL DATABASE` 闪现,最后缩小、失焦、带轻微青色过渡进入白场。进入阵列之后的 reveal/lift/zoom 时间函数未修改。
|
||||
|
||||
参考帧与检查结果见 `verification/BOOT-MOTION.md`。`reference/boot-review.html` 可正常播放、半速、四分之一速度和逐帧核对;原片及提取帧只用于本地验证,不作为产品背景。
|
||||
|
||||
## 2026-09-08 · 品牌排版与 360° 导航
|
||||
|
||||
用户要求放大并叠加原片后进一步校准字重和字间距。以原片 13.48 秒左上角为参考,最终保留 MiSans:品牌容器左 59px、上 114px、宽 270px;第一行 44px / 48px 行高 / 2.5px 字距;第二行 19px / 23px 行高 / 600 字重 / 0.65px 字距;第三行 35px / 40px 行高。ANALYSIS 的八个字符按原片黑色字面起点调整各自间隔,OS 在 270px 容器右端对齐、字距 3px。浏览器测得三行宽度约 269.78、269.63、270px。此为固定品牌字样的排版,不修改字体文件。字体轮廓仍有差异,叠加页保留原片、当前版本及透明度滑杆供复核。
|
||||
|
||||
360° 查看器使用独立目标相机记录 OrbitControls 输入,显示相机对观察中心、球面角度和对数距离进行跟随。平移与缩放的大部分运动在约 200–250ms 内完成;复位采用 560ms 减速曲线和球面短路径,不穿过模型。拖动、滚轮、方向键可从正在复位的当前显示位置接管。进入新档案直接初始化到默认位置,减少动态效果时跳过镜头插值。原有平移半径 5、缩放距离 5–28 的边界保留。
|
||||
|
||||
验证:`reference/viewer-motion-check.html` 的 14 项检查通过,包含 30/120fps 一致性、复位中断、旋转跨正负 180°、连续平移和缩放、Home 精确归位、边界以及重新进入。`reference/brand-review.html` 使用生产界面的共享文字与样式进行 2.4 倍放大及红色叠加。
|
||||
## 2026-09-09 · 可调渲染画质
|
||||
|
||||
用户要求开放更高画质与细分控制。系统设置增加性能、原始、高、极高四个预设及九项独立参数:渲染比例、像素密度上限、SMAA 抗锯齿、纹理过滤、透明材质分辨率、阴影分辨率、环境遮蔽采样与分辨率、景深强度。默认采用原始预设,继续沿用已确认的基线颜色、灯光与镜头。画质仅调整渲染精度与可选效果,不重新制作模型。
|
||||
|
||||
清晰度与材质参数同步至 360° 查看器;阴影、AO 和景深标明仅用于阵列。设置自动保存并兼容原先的高画质开关。面板显示实际渲染尺寸与硬件限制后的纹理倍率;全尺寸缓冲上限为 8,294,400 像素,渲染比例开放至 200%。完整参数、资源生命周期和验证记录见 `verification/RENDER-QUALITY.md`。
|
||||
|
||||
## 2026-09-09 · 原创音效与配乐
|
||||
|
||||
依据原片 6.76–40 秒画面及音轨动态、莱茵生命相关设定,新增原创「观测室」三轨循环配乐与交互音效,后续修订共 17 类。浏览、详情、独立模型查看以及启动阶段采用不同声部比例;用户可以独立控制音乐和音效。旧版静音偏好继续生效。
|
||||
|
||||
设计、参考依据、复现命令、技术验证及听感限制见 `verification/AUDIO-DESIGN.md`。本次未进行人工听辨确认;试听入口为 `reference/audio-check.html`,生成器为 `scripts/render-audio.mjs`。
|
||||
|
||||
### 玻璃交互声音修订
|
||||
|
||||
用户进一步指定玻璃交互应有清脆声音。当前版本采用短起音、不等间隔共鸣和迅速衰减的高频,区分切档轻碰、抽取余音与归位落定。详见 `verification/AUDIO-DESIGN.md` 的玻璃质感修订;用户指定的是声音方向,尚未确认本版听感。
|
||||
|
||||
### 原片逐字输入音效复刻
|
||||
|
||||
依据用户明确要求,逐字输入改用原片约 6.864–6.986 秒的三个 38ms 短音轮替,保留源波形,按实际文字揭示帧触发。输入最短间隔为 24ms,避免 25fps 相邻输入漏拍;新字段首字有反馈,闪动恢复不误触发。提取脚本、源校验、对照试听与限制见 `verification/AUDIO-DESIGN.md`、`public/audio/typing-source.json`。
|
||||
|
||||
### OS 场景的声音语义
|
||||
|
||||
窗口开合使用短电子升降音,启动标志使用低频缓起和声,整块文字显现使用轻微数据脉冲,授权使用双电子短音;逐字输入继续使用原片短音。玻璃共鸣对应档案切换、抽取、归位、阵列入场与拆组。绑定和原片时间码详见 `verification/AUDIO-DESIGN.md`。完整实机试听入口为 `reference/boot-audio.html`,不改写声音偏好;该入口补全此前的开场音效复刻试听。
|
||||
|
||||
## 2026-09-09 · 档案导航文字与刻度
|
||||
|
||||
顶部档案分类、底部列名、悬停标题与权限标签复用主标题的 460ms 文字滚动;内容相同不重复播放,悬停首次出现直接显示。统一处理快速输入、进入详情及减少动态效果。底部八个刻度位置复用节点,跨列保留原有 400ms 伸缩过渡、各列选择记忆及正确的点击映射。实现与 19 项实机检查见 `verification/ROLLING-TITLE.md`。
|
||||
|
||||
## 2026-09-09 · 原片模型细化
|
||||
|
||||
按用户要求放大检查原片 37–39 秒特写与端面,继续通过 Blender MCP 制作。两组环腔改为闭合阶梯截面,细化斜肩、内孔、香槟色嵌件;增加顶部双边压槽、盖板前沿细阶、内周边与接缝,螺钉使用真实凹槽。主模型与六组拆解模型同步更新,保持原有外包围盒、基线配色光照和抽取规则。
|
||||
|
||||
对照入口为 reference/model-review.html,可切换原片、细化前及 2×/3× 放大,使用相同镜头与 1920×1080 缓冲。Blender 源场景与可复现脚本保留。几何变化、检查与局限见 verification/MODEL-REFINEMENT.md;本轮效果尚待用户查看。
|
||||
|
||||
## 2026-09-09 · 清晰内构、解密与深度精度
|
||||
|
||||
按原片 41 秒正面与 46–51 秒多角度细化内部两组环形结构:分段外环、宽斜肩、内孔台阶、橙色嵌带、立面细竖肋与连接薄带。按用户要求完全省略左侧三根高塔。新增顶部压槽、内周边、竖向浮雕字与右下服务孔。通过 Blender MCP 生成,共享 art/clear_reference_details.py,单体与拆解资产几何保持一致。
|
||||
|
||||
黑色解密线按 25 fps 采样重建:34.24–36.04 秒两端沿同一物理对角线向中央延伸,36.04–37.72 秒保持完整,37.72–38.84 秒向中央收束,39.04–39.56 秒逐渐透明。使用单调三次插值,无过冲;原片时间轴可前后跳帧。交互详情在卡片抬起和镜头就位后启动,时间压缩至约 3.63 秒;减少动态效果时直接清晰。
|
||||
|
||||
查看器增加清晰/磨砂两态,透明度与粗糙度连续变化;切换不重置观察位置或拆解进度。退出查看器回到已解密的原档案,退回阵列时恢复磨砂,下一次打开重新解密。
|
||||
|
||||
修正用户报告的大环斑驳闪烁:详情长焦距离约 72,原 near=0.1 导致邻近表面深度量化冲突。near 改为 5、far 保持 300,镜头位置、视场角及构图不变。24 位深度在距离 72 的理论步长由 0.003089 降至 0.00006077。原片 11 个代表镜头的可见模型包围盒最近深度至少 16.66,均在裁剪面之外。
|
||||
|
||||
验证、范围和复现记录见 verification/DECRYPTION.md。逐帧对照入口为 reference/decryption-review.html;实际产品进入详情后可体验,360° 查看器右上角切换玻璃模式。
|
||||
|
||||
## 2026-09-09 · 前段非线性轨迹
|
||||
|
||||
按原片连续帧修订权限圆环、名称黑底及左上角三行文字。三行分别在原片 11.12、11.20、11.28 秒出现,使用独立的先加速后减速位移,第三行到约 12.20 秒落定。黑底在 23.52 秒进入,快速扫过后缓慢收束至 24.40 秒;灰闪时保留遮罩与未覆盖文字。
|
||||
|
||||
外环、白环的半径及弧端独立变化,内环与橙点减速,黑白端点同步缩小。空间运动使用测量关键帧的单调三次插值,离散闪切保持 25 fps 时间码。仍采用原生 DOM/SVG;轨迹见 `src/boot-tracks.ts`,测量脚本及实机验证见 `verification/BOOT-MOTION.md`。本地对照页增加三处局部放大和逐帧入口。
|
||||
|
||||
### 两侧小圆与 Logo 连续轮廓修订
|
||||
|
||||
用户进一步指出两侧小圆以原位旋转和弧段补全为主。原片 544 帧开始出现短弧,左右反向旋转并减速,568 帧分别达到约 332°、339°;随即切至欢迎页。圆心只保留测量到的轻微收拢,中央圆点周围的六颗小点才沿约 35 px 的轨道绕行,小点每隔三帧进入。中央大点保留 546、547、549、550 帧的闪变,然后缩至约 10–11 px 半径。
|
||||
|
||||
Logo 改用共享贝塞尔轮廓的连续路径,绘制首尾独立变速。加号与方点于 247 帧同时出现、向各自位置展开,256 帧后加号转动、方点拉成长横。Logo 左移按 260–294 帧实测位置拟合。425 帧后缺口开始沿轮廓移动,整个标志保持黑色,不再使用整枚透明度闪烁。曲线和复核入口见 `verification/BOOT-MOTION.md`。
|
||||
|
||||
## 2026-09-09 · 内部锐利截面与半透明材质(历史方案,已撤回)
|
||||
|
||||
依据用户新提供的 46–51 秒内部结构近景,专门重做两组环体的截面。宽屋面、直立内外壁、内孔薄台阶和橙色竖向嵌带采用明确的平面与小切边。自定义分裂法线沿圆周插值、在截面折边断开;厚度烘焙正确变换内构法线,同时保留外壳原先的顶点和法线。继续省略左侧高塔。
|
||||
|
||||
两条细窄连接物改为一条连续薄片:大环在 -74°、小环在 215° 附近切向接入,宽度 0.225–0.260,厚度烘焙后 0.008,两端略展宽,中央轻微抬高。全部内构深度在 -0.034 至 0.1424 之间,小于盖板内面 0.174。连接薄片、屋面与壁板分别使用灰色半透明粗糙材质,切边更亮,后方细肋可透出。
|
||||
|
||||
实时渲染采用薄层 alpha 合成与粗糙高光、掠射角密度,再由外盖折射完整内构,避免 WebGL 外盖的 transmission 捕获忽略另一层 transmissive 材质。内部闭合薄片只绘制正面,避免双面透明叠加过度浑浊。该实现是多层玻璃的实时近似,未进行完整体积光线追踪。对照页网格只用于检查透视和层次,不出现在产品中。
|
||||
|
||||
单体与六组拆解模型对应顶点误差 1.688e-7;外壳位置与法线相对打磨前误差均为 0。测试、导出与审阅入口见 verification/INTERNAL-OPTICS.md。
|
||||
|
||||
## 2026-09-09 · 外壳与自上而下的清晰过渡
|
||||
|
||||
根据用户提供的完整正面及四处局部截图,黄色嵌片改为 0.25 × 0.2239,顶边 y=3.7、前面 z=0.206 均与盖板齐平,并建立真实嵌槽。删除左侧黄条、宽端盖及盖板前的额外细阶。螺丝只保留右上和左下两处,使用十字凹槽及齐平垫圈;周围 C 形压槽与上下回路位于前盖之后。模型源脚本 art/shell_reference_details.py 由单体与拆解导出共享。
|
||||
|
||||
原先静态的高度粗糙度渐变被移动的清晰边界取代。选中盖板在进度 0 时整面 roughness=0.48;进度 1 时整面 roughness=0.025。中间羽化宽度为高度的 24%,边界从顶向下走,覆盖刻线与环体。原片时间码 38.84 秒开始,在 39.0 秒达到约 0.51,39.56 秒结束;这是从参考帧观察重建的近似时间曲线。360° 清晰/磨砂切换复用同一材质逻辑。
|
||||
|
||||
局部与前后对照见 reference/shell-review.html,可拖动清晰进度、慢放及改变观察角度。原片同步对照仍在 reference/decryption-review.html。验证见 verification/SHELL-REFINEMENT.md;听感与其他视觉方向的待确认状态不受本次技术验证影响。
|
||||
|
||||
## 2026-09-09 · 恢复第一版完整内构并减轻磨砂
|
||||
|
||||
用户最终指定 reference/internal-review.html 左侧第一版模型,完整恢复 89c7742 的两环截面、白色内沿、橙色嵌环、原始立面分隔、两条弯曲连接带与平滑法线。几何和材质来自 reference/internal-study/assembly-before.glb 对应的源脚本,并通过 Blender MCP 重建单体与拆解资产。此前锐利截面、灰色半透明单连接片及额外细槽方案不再用于产品。
|
||||
|
||||
当前内构世界坐标、法线和原始材质与第一版逐项核对一致,位置/法线比较精度为 1e-5。已经完成的外壳相对 dde61fd 保持一致;该版本快照存放于 reference/ring-study/assembly-before.glb。生成源文件为 art/internal_architecture.py、art/rhine-archive.blend 和 art/archive-assembly.blend。
|
||||
|
||||
磨砂盖板粗糙度由 0.48 减至 0.42,同时修正 Three.js 默认折射 mip 采样在小尺寸预览中相对过重的问题。根据盖板竖轴在当前镜头中的投影长度,将全磨砂采样范围限制在盖板投影高度的约 1.6%。粗糙度下降时模糊范围单调缩小,完全清晰端仍使用原采样;阵列质量为 0 时也使用原采样。保留现有由顶向底的羽化揭示,详情与独立查看器共用。
|
||||
|
||||
对照页左侧明确标记“第一版 · 原始内构”,右侧标记“已恢复 · 第一版内构”,避免把恢复误解为继续细化。原片磨砂对照仍为 reference/decryption-review.html。验证见 verification/RING-TEXTURE.md。
|
||||
|
||||
## 2026-09-09 · 主屏幕与离线
|
||||
|
||||
正式域名为 https://rhine.lubeiluchen.cc/,沿用 Vercel 部署。PWA 使用 standalone 窗口、浅暖色主题与共享莱茵生命图标,iPhone 使用 180 px Apple Touch Icon,其他设备提供 192 / 512 px 和 maskable 图标。状态栏、底部手势和横屏刘海通过安全区域留白处理。
|
||||
|
||||
安装与离线状态集中在设置中。首次联网准备完整资源,当前约 34 MiB,包含字体、模型、声音和 40 份档案;HTML 与资源使用同一个内容版本。下载成功后新版本进入等待状态,用户选择“更新并重启”后应用。下载失败保留完整旧版本,收藏和偏好不随资源更新清除。字体完整分包比此前整包略大,但网页进入前实际只加载约 415 KiB 字体。见 `docs/PWA.md` 与 `verification/STARTUP-LOADING.md`。
|
||||
|
||||
针对更新提示难以操作的反馈,设置图标增加下方的“设置”文字,新版就绪时在底部状态区提供直接更新按钮。弹窗和查看器打开时,外部更新入口遵循原有输入隔离;开场不显示该入口。`update.html` 提供网络恢复入口,供旧版用户更新完整缓存并返回。
|
||||
|
||||
减少动态效果设置补充开机、选档和文字的具体影响,提供用户主动启用完整动效并重播的操作。默认仍尊重系统偏好;正文遮罩由本站最终动效设置控制,避免用户启用完整动效后被系统媒体查询再次隐藏。
|
||||
|
||||
## 2026-09-10 · 阵列悬停、直接拖动与滚轮
|
||||
|
||||
用户最新选择自由平面拖动:鼠标与触摸的屏幕位移同时驱动列与档案两个轨道,拖动中可以自由转弯、反向。选中实际经过的档案,松手后最终吸附到最近位置;滚轮继续只用于列内切档,向上上一档、向下下一档。
|
||||
|
||||
用户试用后进一步要求类似网页快速滑动的手感。松手后保留实际速度并持续减速,快拖和慢拖产生不同滑行距离;去掉最多两列/三档的固定投影上限。选档随实际轨道位置变化,临近停止再吸附。按住正在滑行的阵列立即停止,可从当前显示位置反向或换轴接管。减少动态效果时不继续惯性滚动。
|
||||
|
||||
按住时采样负 X 切列轨道与负 Z 切档轨道的每格 CSS 位移,对两列投影向量组成的 2×2 矩阵求逆,将屏幕位移同时分解到两个轨道。取消角度分类与方向锁定,仅保留 10px 起拖阈值。本次手势内固定投影,旋转屏幕后下一次手势重新采样。自由滑行期间两个速度分量使用相同衰减,较慢分量不会提前吸附而折向另一条轨道;整体放慢后再共同吸附。自由拖动按物理格位选档并更新记忆,避免切列记忆将画面拉离指针;键盘与按钮切列仍恢复记忆。
|
||||
|
||||
悬停卡片额外竖直抬起 0.28,约 200ms 显现,移开平滑回落。悬停高度独立于选中抬起 0.4 与详情抽取 4.05,不改变镜头进度;所有网格部件一起移动。详情与独立查看器继续采用已确认的旋转、缩放和拆解操作。实现与验证见 `verification/ARRAY-INPUT.md`。
|
||||
|
||||
## 工作台沿用主页视觉
|
||||
|
||||
用户于 2026-09-10 指出首版绿色强调色与原主页不一致。工作台沿用原主页近黑正文(#080a08)、暖灰辅助文字(#77756d)、灰色细线(#aaa59a)及暖棕悬停(#9b7247)。取消新增的绿色填充、面板底块和导航色块;使用透明内容、细线分隔与原阵列长短竖线刻度表达选择。保留原字体、模型、灯光与阵列动效。
|
||||
|
||||
## 明暗配色
|
||||
|
||||
暗色以 #11181b 为背景,#e0e3dc 为正文,#a6b0b1 为辅助文字,#536166 为分隔线,#c5a16b 为琥珀强调。保留原版排版与布局,不使用全屏反色。三维阵列使用独立的烟灰材质端点及冷灰边缘,清晰盖板随原有自上而下解密前沿恢复接近无色的透射,避免遮黑内部结构。
|
||||
|
||||
切换以选中单元为起点:每行延迟 34ms、每列 110ms,最远延迟 600ms;单卡过渡 580ms,背景 850ms。切换不附加升降或位移,快速反向接续各卡当前颜色。减少动态效果直接应用目标配色。
|
||||
|
||||
## 自适应阵列覆盖
|
||||
|
||||
交互阵列根据渲染当帧完成插值后的相机重新计算覆盖范围。截取相机视锥与阵列可占据的高度区间,换算为行列候选,再用完整卡片边界裁剪,连续紧凑写入实例矩阵。鼠标点击与小游戏目标使用同一份实际实例映射。相机移动、屏幕比例变化与档案轨道移动都在当前帧更新。
|
||||
|
||||
默认保留 18% 的屏幕方向余量,额外档为 50%;保留附近阴影贡献,远处完全雾化后不继续扩展无限阵列。实例缓冲按需增长并复用容量,实际绘制数独立于缓冲容量。既有 Blender 几何与材质细节不变。
|
||||
|
||||
暗色雾颜色调整为 #263136,预览雾起点为相机至目标距离加 1,终点加 16;浅色仍为加 5/加 25。详情过渡到原来的加 -1/加 12 距离。暗色增加顶部和底部渐隐,使边缘轮廓融入背景。原片固定舞台仍维持原始阵列位置。
|
||||
|
||||
工作台细节、退场及连续频谱 A/B 的当前规范见 docs/WALLPAPER-ENGINE.md 与 verification/WORKBENCH-REVISION.md。
|
||||
|
||||
## 可选界面景深与质感
|
||||
|
||||
壁纸 UI 使用共同径向曲面,按各区块的四角计算投影矩阵,左/右与上/下产生相反的斜率;替代此前同方向平移与倾斜。纵深默认 20%,曲率系数为纵深乘 0.08,屏幕四角约束保持边距;鼠标追踪独立开启,切向项系数 0.0075,平滑速率 7。浏览器原生变换同时更新点击区域。工作台入场使用独立 translate 与投影叠加。
|
||||
|
||||
全局 SVG 合成滤镜使用 sRGB,覆盖三维、DOM 与弹窗。色散在画面边缘减弱至零以避免采样越界彩框,默认 20%;颗粒默认 20%、尺寸 20%,以 12Hz 更新,减少动态效果时冻结;暗角默认 20%。滑杆高值提供明显的校准范围。局部磨砂最大 24px 模糊和 0.65 主题底色不透明度,默认 55%。三种总开关仍默认关闭。详见 verification/VISUAL-EFFECTS.md。
|
||||
|
||||
用户配置的四边距作为原始 UI 框架的额外偏移,范围 −300~300px、默认 0,正值向内。通过 --stage-scale 抵消基准舞台缩放;布局先按新边界定位,再投影 UI 曲面,保持字号。背景、卡片及场景坐标标记继续使用完整视口。详情的轻移使用独立 translate,与曲面矩阵叠加。
|
||||
|
||||
|
||||
壁纸下边距最新规则:底部两排在 HUD 投影之后整体平移,相对间距固定;中部不因下边距移动或产生滚动区域。暗色新增可选的仅选中标签强调,其余 Index_Inlay 使用深灰;默认关闭保留原琥珀标签。
|
||||
|
||||
|
||||
## 超级性能模式与自定义画质
|
||||
|
||||
用户于 2026-09-10 要求低配壁纸模式与减少动态效果独立,并恢复 WE 原生自定义画质。新增「超级性能模式(保留动态效果)」开关,默认关闭;开启时以独立的有效画质覆盖已保存画质,不改减少动态效果、宿主帧率或音乐律动设置,关闭恢复当前预设/自定义配置。
|
||||
|
||||
超级模式:三维比例 60%、DPR 上限 1、总像素不超过 921600;关闭阵列阴影、AO、景深和 SMAA,直接输出主场景;背景阵列使用无折射、无清漆材质并隐藏小螺丝,选中/详情模型保留完整结构和低分辨率折射。全局屏幕滤镜暂时停用,文字磨砂改为无模糊底色,HUD 曲面与追踪继续运行。DOM 文字不降低分辨率。
|
||||
|
||||
WE 画质下拉增加「自定义」,仅此选择且超级模式关闭时显示九项控制:渲染比例、像素密度、抗锯齿、阴影、AO 采样、AO 分辨率、景深、透明材质分辨率、纹理过滤。连续参数用滑杆,离散参数用原生下拉。部分宿主回调保留其他参数;超级模式开启期间保留自定义更新,关闭恢复最新配置。完整证据见 verification/SUPER-PERFORMANCE.md。
|
||||
|
||||
## 三维资源开关与自定义壁纸
|
||||
|
||||
- 用户于 2026-09-10 授权在左下 SESSION 右侧增加 3D 开关:档案错峰下落退场后实际释放全部三维场景、查看器和 WebGL 资源;再次开启重新加载并从下方逆向升起,保留选档。关闭途中可反向恢复,减少动态效果时直接切换。
|
||||
- 3D 退场、关闭与载入期间隐藏游戏入口及模型专用入口;重新开启按原显示偏好恢复。2D 工作台和正文继续可用。
|
||||
- WE「入场与画面」增加可选自定义壁纸,默认不启用;用户选图后,仅在三维资源释放后淡入。重新载入成功时图片淡出,失败保持图片并允许重试。沿用原生实现,见 verification/THREE-RELEASE.md。
|
||||
- 用户明确保留三种音乐律动并列选择:原版、连续频谱、层叠流波。原版仍为默认,不再标为待定 A/B。
|
||||
- 修复壁纸自身交互声音触发音乐归齐:静音状态抑制本地音效引起的新频谱活动,已建立的音乐继续响应。全部归齐时取消标签强调,暗色仅选中强调随实际升降连续过渡,见 verification/SELECTION-STATE.md。
|
||||
|
||||
## 开场中央字体补充(2026-09-11)
|
||||
|
||||
后续从 MyFonts 取得正式 Webfont 后,本机使用原始 WOFF2 原生字形;既有轮廓仅作为无本地许可包或加载失败的后备。复用逐字尺寸和基线,不重新调整已确认排版。授权字体单独存放并排除公开 Git。
|
||||
|
||||
中央访问、身份、请求、处理、授权提示采用 Novecento Sans Wide Normal,欢迎三行采用 Bold。按固定文案制作轮廓,保留原字距动画与逐字时间轴;处理提示字号 21.35px,顶部 527px,图标间距 10px(1920×1080 基准)。左上 RHINE LAB 按用户后续纠正改用 DemiBold,字号 50.75px、字距 1px,标题行固定高 48px,图形顶部偏移 1.425px;下方两行保留 MiSans 与既有位置。来源与验证见 verification/BOOT-LETTERING.md。
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 LBEILC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,297 @@
|
||||
# RHINE LAB · ANALYSIS OS
|
||||
|
||||
**把莱茵生命的终端,做成可以操作的三维界面。**
|
||||
|
||||
**[在线体验 → rhine.lubeiluchen.cc](https://rhine.lubeiluchen.cc/)**
|
||||
|
||||
iPhone 可用 Safari 打开在线版,通过“分享 → 添加到主屏幕”安装;从主屏幕图标进入可使用独立窗口。首次联网后,设置中显示“离线资源已就绪”即可离线浏览档案和模型。支持桌面不同比例、手机横竖屏和触摸操作。[安装与更新说明](docs/PWA.md)
|
||||
|
||||

|
||||
|
||||
这是对《明日方舟》特别映像「莱茵生命:访问」终端界面的非官方复刻。从白底开场进入五列循环档案阵列,抽取一份档案,等待玻璃与正文解密,再进入独立查看器观察内部结构。
|
||||
|
||||
项目以原 PV 的 **5–40 秒**为主要视觉与动效参考,实际开场从 **6.76 秒的白色画面**开始;内部结构另参考约 **41 秒及 46–51 秒**的正面与多角度画面。检索、收藏、正文阅读、结构拆解与声音设置是可操作的扩展功能。
|
||||
|
||||
代码由 GPT-6 Astra 协助完成,模型通过 Blender MCP 制作。界面采用 **TypeScript + Three.js + Vite**,运行时实时渲染三维模型,开场由 DOM / SVG 与场景时间轴驱动。
|
||||
|
||||
[快速运行](#快速运行) · [界面与动效](#界面与动效) · [操作说明](#操作说明) · [源码与 Blender 工程下载](https://pan.quark.cn/s/762d9ee9dfc3) · [参考原 PV](https://www.bilibili.com/video/BV1rr4y1b7sz/)
|
||||
|
||||
## Wallpaper Engine 壁纸与独立仓库
|
||||
|
||||
壁纸版已从本项目拆分为独立公共仓库,后续功能在 **[RhineLabWallpaper](https://github.com/LBEILC/RhineLabWallpaper)** 的 `main` 分支开发。本仓库继续维护网页版本;[原壁纸分支 codex/wallpaper-engine](https://github.com/LBEILC/RhineLabUI/tree/codex/wallpaper-engine) 保留迁移记录。需要最新壁纸源码,请前往独立仓库。
|
||||
|
||||
**[在 Steam 创意工坊订阅壁纸 → Rhine Lab · 莱茵生命交互桌面](https://steamcommunity.com/sharedfiles/filedetails/?id=3799142774)**
|
||||
|
||||

|
||||
|
||||
- **两种工作模式**:三维档案展示与桌面工作台,可显示时间日期、今日事项、日程倒计时、系统媒体信息和专注计时。
|
||||
- **壁纸交互**:音乐律动、呼吸效果、波纹接力小游戏,以及亮暗配色、玻璃模糊、HUD 曲面与视差。
|
||||
- **按需保留画面**:在 WE 属性中选择显示哪些组件;可关闭启动时加载 3D,播放 2D 开场后直接进入工作台,再从左下角手动载入模型。
|
||||
- **自定义图片**:关闭 3D 后可显示自行选择的壁纸,支持调整上下遮罩范围,设为 0 即关闭;时钟和媒体信息复用滚动数字与文字效果。
|
||||
- **宿主适配**:接入 WE 属性、音频响应、媒体信息、帧率及暂停通知,壁纸资源本地打包,壁纸构建不启用 PWA。
|
||||
|
||||
使用壁纸请通过上方创意工坊链接订阅,在 Wallpaper Engine 中应用并调整属性。需要自行构建时:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/LBEILC/RhineLabWallpaper.git
|
||||
cd RhineLabWallpaper
|
||||
npm ci
|
||||
npm run build:wallpaper
|
||||
```
|
||||
|
||||
输出目录为 `release/wallpaper`,在 Wallpaper Engine 编辑器中打开其中的 `index.html`。系统媒体信息取决于播放器支持及 WE 媒体集成设置。更多细节见 [壁纸使用与开发说明](docs/WALLPAPER-ENGINE.md) 和 [创意工坊发布说明](docs/WORKSHOP-PUBLISH.md)。
|
||||
|
||||
以下为项目共用的网页与三维档案功能介绍。
|
||||
|
||||
|
||||
## 新版效果
|
||||
|
||||
- **亮暗配色**:设置中切换,卡片依次变色,详情与独立查看器同步适配。
|
||||
- **自适应阵列**:按实际镜头与屏幕范围布置、裁剪档案,改善宽屏边角露出尽头;开场铺满实际视口。
|
||||
- **超级性能模式**:设置中独立开启,降低三维渲染负担并保留动效,关闭恢复之前的画质。
|
||||
- **数字时钟**:页脚时、分、秒独立滚动,修复变换布局下部分数字动画中断。
|
||||
|
||||
整合范围与验证见 [主分支整合记录](verification/WEB-INTEGRATION.md)。
|
||||
|
||||
- **模型与正文同步解密**:对角解密线合拢、保持并收束,盖板自上而下由磨砂变清晰;右侧文档的遮挡条随之退开,露出标题、资料字段与正文。
|
||||
- **可看清的双环内构**:双环、连接带与白色/橙色部件封装在盖板与基板之间。顶边黄色方块、两处螺丝和盖板后的刻线补全了外壳细节。
|
||||
- **清晰/磨砂切换**:独立查看器可以随时改变玻璃状态,保留当前视角和拆解位置;支持平滑缩放、平移及复位。
|
||||
- **滚动文字与编号**:档案标题、分类、权限标签及编号连续滚动,快速输入衔接最新选择。
|
||||
- **重新校准的开场**:逐字输入、圆环绕行、连续 Logo 笔画、身份验证和欢迎转场,按原片逐帧修订轨迹与节奏。
|
||||
- **声音与画质设置**:玻璃交互音、系统电子音与三轨循环配乐;音效和音乐可独立开关、调节音量。画质提供四档预设及精细设置。
|
||||
|
||||
## 界面与动效
|
||||
|
||||
以下截图与动图于 **2026-09-09** 重新采集,均来自当前版本的实际浏览器运行。截图为 **1600 × 900**,GIF 为 **8–12 fps**、原速播放;压缩后的帧率与颜色不代表实时渲染质量。采集版本与复现步骤见 [素材说明](docs/media/README.md)。
|
||||
|
||||
### 抽取、解密与阅读
|
||||
|
||||
档案竖直升起,镜头靠近并转向详情构图。解密时玻璃与文档一起揭示;完成后可阅读概述、研究记录及访问日志,也可以收藏或导出 UTF-8 文本。
|
||||
|
||||

|
||||
|
||||
| 解密完成 · 清晰内构与档案概述 | 研究记录 · 正文阅读 |
|
||||
| --- | --- |
|
||||
| [](docs/media/detail.jpg) | [](docs/media/research.jpg) |
|
||||
|
||||
### 清晰内构与磨砂玻璃
|
||||
|
||||
在 360° 查看器中比较两种玻璃状态。切换不会重置镜头或拆解状态,返回详情后仍保留已解密状态。
|
||||
|
||||
| 清晰 · 双环与连接带 | 磨砂 · 轻柔折射 |
|
||||
| --- | --- |
|
||||
| [](docs/media/viewer-clear.jpg) | [](docs/media/viewer-frosted.jpg) |
|
||||
|
||||
<details>
|
||||
<summary><strong>查看动图:清晰/磨砂连续切换</strong></summary>
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
### 360° 旋转、拆解与重组
|
||||
|
||||
紧固件、透明盖板、折射环组、光学核心、信息基板、背板与框架按六组展开。拆解后仍可旋转、平移和缩放,观察部件之间的关系,再一键重组。
|
||||
|
||||
[](docs/media/assembly.jpg)
|
||||
|
||||
<details>
|
||||
<summary><strong>查看动图:模型拆解、旋转与重组</strong></summary>
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
### 循环阵列与滚动标题
|
||||
|
||||
五类、每类八份,共 **40 份档案**。上下翻阅、左右切列均可持续循环;切回某列时保留上次选择。选中抬起与阵列波浪同时开始,标题和编号跟随输入滚动;返回阵列时,档案先转正再下降。
|
||||
|
||||
<details>
|
||||
<summary><strong>查看动图:切列、翻阅与连续文字滚动</strong></summary>
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
### 白底开场
|
||||
|
||||
从终端逐字输入到圆环、Logo 绘制,再进入身份接入与权限验证。可以重播,也可以跳过开场直接进入阵列。
|
||||
|
||||
[](docs/media/boot.jpg)
|
||||
|
||||
<details>
|
||||
<summary><strong>查看动图:新版开场片段</strong></summary>
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
### 检索、声音与画质
|
||||
|
||||
检索支持编号、标题、英文名、科室、相关人物与分类筛选。收藏和设置保存在当前浏览器中。音效区分档案的玻璃碰触与系统操作的电子反馈,背景配乐随开场、阵列、详情和查看器调整三轨比例。
|
||||
|
||||
| 档案索引 · 关键词与分类筛选 | 系统设置 · 声音与画质 |
|
||||
| --- | --- |
|
||||
| [](docs/media/search.jpg) | [](docs/media/settings.jpg) |
|
||||
|
||||
GIF 不含声音。可单独[试听原创配乐「观测室」](public/audio/observatory-preview.mp3),完整声音效果请启动应用体验。浏览器可能需要一次点击或按键才允许播放音频。
|
||||
|
||||
## 快速运行
|
||||
|
||||
需要 **Node.js 22.12 或更高版本**(可使用 Node.js 24),以及支持 WebGL 2 的现代桌面浏览器。首次安装依赖需要网络;应用不需要 API Key,也不需要启动后端服务。
|
||||
|
||||
### 获取项目
|
||||
|
||||
```sh
|
||||
git clone https://github.com/LBEILC/RhineLabUI.git
|
||||
cd RhineLabUI
|
||||
```
|
||||
|
||||
也可以从 GitHub 的 **Code → Download ZIP** 下载当前源码,或获取[夸克项目包](https://pan.quark.cn/s/762d9ee9dfc3)。夸克包是 **2026-09-08 的打包快照**,包含源码、运行模型与 Blender 源工程;后续更新以本仓库为准。
|
||||
|
||||
### 安装并启动
|
||||
|
||||
```sh
|
||||
npm ci
|
||||
npm run dev
|
||||
```
|
||||
|
||||
打开终端显示的地址,通常为 `http://127.0.0.1:5173/`。如果端口被占用,以终端实际输出为准。
|
||||
|
||||
Windows 用户安装 Node.js 并解压项目后,也可以双击 [`启动终端.cmd`](启动终端.cmd):首次运行会安装依赖,然后启动本地服务并打开浏览器。
|
||||
|
||||
### 构建与预览
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run preview
|
||||
```
|
||||
|
||||
生产文件输出到 `dist/`,可以交给静态 HTTP 服务托管。请通过服务地址访问,不要直接双击 `dist/index.html`。
|
||||
|
||||
## 操作说明
|
||||
|
||||
### 终端与档案
|
||||
|
||||
| 操作 | 效果 |
|
||||
| --- | --- |
|
||||
| 开场中按 `Enter` / `Esc`,或点击 `ENTER SYSTEM` | 资源就绪后进入交互阵列 |
|
||||
| `←` / `→` | 切换档案类别,首尾循环 |
|
||||
| `↑` / `↓` | 翻阅同类档案,首尾循环 |
|
||||
| `Enter`、`ACCESS FILE` 或文件编号 | 读取当前档案 |
|
||||
| 在详情模型上拖动 | 档案获得净空后,旋转观察 |
|
||||
| `/` 或 `ARCHIVE INDEX` | 打开检索,可搜索编号、标题、英文名、科室、负责人和分类 |
|
||||
| `SAVE ARCHIVE` / `SAVED` | 收藏当前档案 / 查看收藏 |
|
||||
| `EXPORT` | 下载当前档案的文本文件 |
|
||||
| `Esc` | 关闭当前弹窗,或从详情返回阵列 |
|
||||
|
||||
### 独立模型查看器
|
||||
|
||||
在详情页点击 **「360° 查看文档模型」** 进入。
|
||||
|
||||
| 操作 | 效果 |
|
||||
| --- | --- |
|
||||
| 鼠标拖动 | 环绕旋转模型 |
|
||||
| 滚轮、`+` / `−` | 平滑缩放 |
|
||||
| 方向键 | 平移观察位置 |
|
||||
| 「复位视角」或 `Home` | 平滑恢复初始观察位置 |
|
||||
| 「清晰」 / 「磨砂」 | 切换玻璃状态,保留视角和拆解位置 |
|
||||
| 「拆解档案」 / 「一键重组」 | 展开六组部件 / 连续收回 |
|
||||
| `Esc` 或「返回档案」 | 关闭查看器,返回原档案 |
|
||||
|
||||
### 显示与偏好
|
||||
|
||||
布局以 **1920 × 1080** 为基准等比例适应窗口,主要面向桌面与横向屏幕。设置页提供音效与背景音乐的独立开关、独立音量,以及减少动态效果、画质、全屏和重新播放开场。收藏和偏好保存在当前浏览器中。
|
||||
|
||||
首次载入需要加载字体与 GLB 模型。项目保留了四份官方 MiSans WOFF2,合计约 19.7 MB,按实际使用加载。画质预设为**性能、原始、高、极高**,默认使用原始;精细设置可调整渲染比例、像素密度、抗锯齿、纹理过滤、透明材质分辨率、阴影、环境遮蔽及景深。运行不够流畅时可选性能档;需要简化镜头、文字与揭示动画时,可启用减少动态效果。
|
||||
|
||||
## 工程结构
|
||||
|
||||
| 目录或文件 | 内容 |
|
||||
| --- | --- |
|
||||
| [`src/main.ts`](src/main.ts) | 页面状态、档案阅读、检索、收藏与快捷键 |
|
||||
| [`src/boot.ts`](src/boot.ts)、[`src/boot-motion.ts`](src/boot-motion.ts) | 开场界面与逐帧时间轴 |
|
||||
| [`src/scene.ts`](src/scene.ts)、[`src/archive-loop.ts`](src/archive-loop.ts) | Three.js 场景、循环阵列、抽取与归位 |
|
||||
| [`src/model-viewer.ts`](src/model-viewer.ts) | 独立模型查看器与拆解动画 |
|
||||
| [`src/decryption.ts`](src/decryption.ts)、[`src/document-decryption.ts`](src/document-decryption.ts) | 模型解密轨迹与正文同步揭示 |
|
||||
| [`src/audio.ts`](src/audio.ts)、[`public/audio/`](public/audio/) | 交互音效、三轨配乐与音源记录 |
|
||||
| [`src/render-quality.ts`](src/render-quality.ts)、[`src/quality-renderer.ts`](src/quality-renderer.ts) | 画质预设与渲染管线 |
|
||||
| [`content/archives.json`](content/archives.json) | 页面与下载共用的五类、40 份档案数据 |
|
||||
| [`src/data.ts`](src/data.ts) | 档案类型与阵列位置映射 |
|
||||
| [`public/assets/`](public/assets/) | 运行所需的 GLB 模型 |
|
||||
| [`public/archives/`](public/archives/) | 导出的档案文本;启动和构建前自动生成 |
|
||||
| [`art/`](art/) | Blender 源文件、建模与审阅脚本 |
|
||||
| [`scripts/`](scripts/) | 档案导出与行为检查 |
|
||||
| [`reference/`](reference/)、[`verification/`](verification/) | 开发对照工具与分阶段验证记录 |
|
||||
| [`docs/media/`](docs/media/) | README 截图与动图 |
|
||||
| [`DESIGN.md`](DESIGN.md) | 视觉、相机、材质与运动约束 |
|
||||
|
||||
原片时间轴使用 160 个阵列位置;交互模式使用固定的可见窗口与外围卡片补位,让有限的档案内容可以持续循环。
|
||||
|
||||
### 修改与复核
|
||||
|
||||
修改档案内容从 [`content/archives.json`](content/archives.json) 入手,字段与操作步骤见 [档案修改说明](content/README.md)。`npm run dev` 与 `npm run build` 会先校验数据,再更新 `public/archives/` 中的文本导出;开发过程中修改数据后,可执行 `npm run export:archives` 同步下载文件。`npm run check:content` 检查数据规则与导出一致性。
|
||||
|
||||
```sh
|
||||
node scripts/check-motion.mjs
|
||||
node scripts/check-loop.mjs
|
||||
node scripts/check-appearance.mjs
|
||||
node scripts/check-assembly.mjs
|
||||
node scripts/check-decryption.mjs
|
||||
node scripts/check-shell.mjs
|
||||
node scripts/check-internal-optics.mjs
|
||||
node scripts/check-quality.mjs
|
||||
```
|
||||
|
||||
这些脚本检查运动、循环位置、外观、装配结构、解密轨迹、外壳、内构与画质参数。视觉效果仍需在浏览器中实际查看,尤其是快速切换、模型归位、文档揭示及查看器进出过渡。
|
||||
|
||||
| 本地调试路径 | 用途 |
|
||||
| --- | --- |
|
||||
| `/?scene=archive` | 直接进入档案阵列 |
|
||||
| `/?scene=detail` | 直接进入档案详情 |
|
||||
| `/?time=28&freeze=1` | 固定在参考时间轴的指定时刻 |
|
||||
| `/reference/review.html`、`/reference/boot-review.html` | 原片与复刻对照工具 |
|
||||
| `/reference/decryption-review.html` | 玻璃解密逐帧对照 |
|
||||
| `/reference/document-decryption-check.html` | 正文同步解密与布局检查 |
|
||||
| `/reference/boot-audio.html` | 完整开场声音试听 |
|
||||
|
||||
原 PV 不随仓库分发。使用视频对照工具时,需要自行准备对应参考视频;正常运行应用不依赖它。
|
||||
|
||||
### Blender 源工程
|
||||
|
||||
| 文件 | 用途 |
|
||||
| --- | --- |
|
||||
| [`art/rhine-archive.blend`](art/rhine-archive.blend) | 档案盒基础模型与审阅灯光 |
|
||||
| [`art/archive-assembly.blend`](art/archive-assembly.blend) | 可按六组结构拆解的模型 |
|
||||
| [`art/build_archive.py`](art/build_archive.py) | 生成基础模型与 GLB |
|
||||
| [`art/build_assembly.py`](art/build_assembly.py) | 生成拆解模型与 GLB |
|
||||
| [`art/internal_architecture.py`](art/internal_architecture.py) | 当前双环内构与连接带 |
|
||||
| [`art/shell_reference_details.py`](art/shell_reference_details.py) | 顶边方块、螺丝及盖板后刻线 |
|
||||
| [`art/setup_studio.py`](art/setup_studio.py) | 配置资产审阅灯光与相机 |
|
||||
|
||||
普通运行直接使用现有 GLB 即可,无需安装 Blender。重新建模时,可在 Blender 的脚本环境中通过 `runpy.run_path()` 执行对应脚本,或通过 Blender MCP 调用。脚本根据自身位置确定项目目录,重新生成会更新对应模型输出。
|
||||
|
||||
## 参考与资源说明
|
||||
|
||||
参考作品为《明日方舟》特别映像「莱茵生命:访问」:[BV1rr4y1b7sz](https://www.bilibili.com/video/BV1rr4y1b7sz/)。本项目与官方制作方无隶属关系,原 PV、相关名称、标志与设定的权利归各自权利人所有。原片未展示的档案摘要、日期、研究记录等属于本项目的扩展演示内容。
|
||||
|
||||
模型为重新制作;实时折射、景深、灯光与局部细节和原 PV 仍有差异。身份验证画面是演示状态机,不连接真实身份或业务服务。
|
||||
|
||||
- **MiSans**:使用小米官方字体文件,保留[字体许可](public/fonts/MiSans-license.pdf)及字体目录内的版权说明,设置页也提供署名与许可入口。
|
||||
- **Rolling Number**:用于编号和文字滚动,许可见 [`public/licenses/rolling-number.txt`](public/licenses/rolling-number.txt)。
|
||||
- **声音**:三轨配乐为本项目程序编配;逐字输入使用原 PV 的三个 38ms 短音,来源与处理记录见 [音频说明](public/audio/README.md)。原片短音及其衍生片段不纳入原创配乐的 MIT 授权声明。
|
||||
- **其他依赖**:各自遵循其原有许可。源码公开不改变第三方资源的权利。
|
||||
|
||||
源码包包含运行代码、模型、Blender 工程、说明与验证脚本,不包含依赖目录、本机缓存、原 PV 或完整录制素材。
|
||||
|
||||
## 开源许可
|
||||
|
||||
本项目自行编写且有权授权的程序代码、建模脚本及配套技术文档采用 [MIT License](LICENSE),版权署名为 **Copyright (c) 2026 LBEILC**。你可以使用、修改、分发这些内容,也可以将其用于商业或闭源项目;分发代码或其重要部分时,须保留版权声明和许可证。软件按原样提供,不作担保,具体以许可证全文为准。
|
||||
|
||||
MIT 授权不覆盖第三方权利或自动覆盖仓库内全部素材:
|
||||
|
||||
- 《明日方舟》及莱茵生命相关名称、标志、设定、原 PV 和原作视觉设计,以及它们在模型、界面、截图或演示文本中的呈现,不因本项目公开而获得额外授权。本项目无法代替相应权利人授予这些权利。
|
||||
- Blender / GLB 模型、图像、动图等非代码资产未另行声明为 MIT;建模脚本采用 MIT 不表示脚本生成的原作相关视觉内容也已获授权。
|
||||
- MiSans、Rolling Number 及其他第三方依赖继续遵循各自的许可证和版权声明,见上方「参考与资源说明」。
|
||||
|
||||
复用代码时,请根据用途处理涉及的第三方素材与标志。GitHub 当前源码包已包含本许可证;上方夸克链接为早期打包快照,未包含本次新增的项目许可证文件,最新许可说明以本仓库为准。
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"root": "E:\\归档",
|
||||
"port": 43117,
|
||||
"exclude": [],
|
||||
"indexFile": "public/archive-index.json",
|
||||
"logFile": "00_索引\\操作日志.jsonl"
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,208 @@
|
||||
import bpy, math, os
|
||||
from mathutils import Vector
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = str(Path(__file__).resolve().parents[1])
|
||||
scene = bpy.data.scenes.new('Rhine_Archive_Work')
|
||||
bpy.context.window.scene = scene
|
||||
for old in list(bpy.data.scenes):
|
||||
if old != scene and old.name.startswith('Rhine_Archive_Asset'):
|
||||
for obj in list(old.objects):
|
||||
if len(obj.users_scene)==1:bpy.data.objects.remove(obj,do_unlink=True)
|
||||
bpy.data.scenes.remove(old)
|
||||
scene.name='Rhine_Archive_Asset'
|
||||
for m in list(bpy.data.materials):
|
||||
if m.users==0:bpy.data.materials.remove(m)
|
||||
|
||||
def material(name, color, rough=.3, metal=0, transmission=0):
|
||||
m=bpy.data.materials.new(name); m.diffuse_color=(*color,1); m.use_nodes=True
|
||||
p=m.node_tree.nodes.get('Principled BSDF')
|
||||
p.inputs['Base Color'].default_value=(*color,1)
|
||||
p.inputs['Roughness'].default_value=rough
|
||||
p.inputs['Metallic'].default_value=metal
|
||||
p.inputs['Transmission Weight'].default_value=transmission
|
||||
p.inputs['IOR'].default_value=1.46
|
||||
return m
|
||||
|
||||
shell=material('Frosted_Polymer',(.985,.975,.963),.36,0,.78)
|
||||
edge=material('Ivory_Edges',(.94,.916,.892),.28,.02,.72)
|
||||
core=material('Internal_Ceramic',(.74,.705,.68),.52,.06)
|
||||
metal=material('Titanium_Fasteners',(.58,.60,.61),.19,.82)
|
||||
gold=material('Champagne_Index',(.64,.46,.29),.33,.48)
|
||||
paper=material('Printed_Label',(.91,.89,.84),.65)
|
||||
diffuser=material('Optical_Diffuser',(.925,.902,.881),.67,0,0)
|
||||
optics=material('Subsurface_Optics',(.70,.675,.66),.39,.12)
|
||||
optical_edge=material('Optical_Edges',(.94,.92,.90),.3,.03,.45)
|
||||
ink=material('Carbon_Ink',(.025,.026,.023),.75)
|
||||
|
||||
def cube(name, loc, size, mat, bevel=.015):
|
||||
bpy.ops.mesh.primitive_cube_add(size=1, location=loc)
|
||||
o=bpy.context.object; o.name=name; o.dimensions=size
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
o.data.materials.append(mat)
|
||||
if bevel:
|
||||
m=o.modifiers.new('Precision radiused edge','BEVEL'); m.width=bevel; m.segments=3
|
||||
bpy.context.view_layer.objects.active=o; bpy.ops.object.modifier_apply(modifier=m.name)
|
||||
o.modifiers.new('Weighted corner normals','WEIGHTED_NORMAL')
|
||||
return o
|
||||
|
||||
def torus(name,x,z,radius,tube,mat,y=-.091):
|
||||
bpy.ops.mesh.primitive_torus_add(major_radius=radius,minor_radius=tube,major_segments=64,minor_segments=10,location=(x,y,z),rotation=(math.pi/2,0,0))
|
||||
o=bpy.context.object; o.name=name; o.data.materials.append(mat)
|
||||
for p in o.data.polygons:p.use_smooth=True
|
||||
return o
|
||||
|
||||
def text(name, body,x,z,size,mat=ink):
|
||||
c=bpy.data.curves.new(name,'FONT'); c.body=body;c.size=size;c.extrude=.0002;c.space_character=1.05
|
||||
o=bpy.data.objects.new(name,c);scene.collection.objects.link(o)
|
||||
o.location=(x,-.123,z);o.rotation_euler=(math.pi/2,0,0);c.materials.append(mat)
|
||||
return o
|
||||
|
||||
def annular_profile(name, x, z, profile, mat, segments=128, start=0, end=2*math.pi, sharp=False):
|
||||
# Closed revolved cross-section: a shallow moulded lens, not a round tube.
|
||||
vertices=[]; faces=[]; n=len(profile)
|
||||
closed=abs(end-start-2*math.pi)<1e-6
|
||||
segments=max(8,math.ceil(segments*(end-start)/(2*math.pi)))
|
||||
rows=segments if closed else segments+1
|
||||
for i in range(rows):
|
||||
a=start+(end-start)*i/segments
|
||||
for r,y in profile: vertices.append((x+r*math.cos(a),y,z+r*math.sin(a)))
|
||||
for i in range(segments):
|
||||
for j in range(n):
|
||||
faces.append((i*n+j,((i+1)%rows)*n+j,((i+1)%rows)*n+(j+1)%n,i*n+(j+1)%n))
|
||||
if not closed:
|
||||
faces.extend([tuple(reversed(range(n))),tuple(segments*n+j for j in range(n))])
|
||||
mesh=bpy.data.meshes.new(name);mesh.from_pydata(vertices,[],faces);mesh.update()
|
||||
obj=bpy.data.objects.new(name,mesh);scene.collection.objects.link(obj);mesh.materials.append(mat)
|
||||
# The clockwise section above yields outward normals, including the bore.
|
||||
for p in mesh.polygons:p.use_smooth=len(p.vertices)==4
|
||||
if sharp:
|
||||
# Keep each section edge hard, but interpolate around the circumference.
|
||||
# Flat shading alone would facet the circle; all-smooth shading balloons
|
||||
# the roof/wall junction into a rounded tube.
|
||||
normals=[]
|
||||
for face in mesh.polygons:
|
||||
if face.index>=segments*n:
|
||||
face.use_smooth=False
|
||||
normals.extend([tuple(face.normal)]*len(face.loop_indices))
|
||||
continue
|
||||
j=face.index%n
|
||||
dr=profile[(j+1)%n][0]-profile[j][0]
|
||||
dy=profile[(j+1)%n][1]-profile[j][1]
|
||||
for loop in face.loop_indices:
|
||||
row=mesh.loops[loop].vertex_index//n
|
||||
angle=start+(end-start)*row/segments
|
||||
normal=Vector((-dy*math.cos(angle),dr,-dy*math.sin(angle))).normalized()
|
||||
normals.append(tuple(normal))
|
||||
mesh.normals_split_custom_set(normals)
|
||||
return obj
|
||||
|
||||
def channel(name, points, depth, radius, mat):
|
||||
curve=bpy.data.curves.new(name,'CURVE');curve.dimensions='3D'
|
||||
curve.resolution_u=8;curve.bevel_depth=radius;curve.bevel_resolution=2
|
||||
spline=curve.splines.new('POLY');spline.points.add(len(points)-1)
|
||||
for point,(x,z) in zip(spline.points,points):point.co=(x,depth,z,1)
|
||||
obj=bpy.data.objects.new(name,curve);scene.collection.objects.link(obj);curve.materials.append(mat)
|
||||
return obj
|
||||
|
||||
front_cover=cube('Front frosted optical cover',(0,-.095,1.85),(5,.016,3.7),shell,.007)
|
||||
cube('Rear translucent carrier',(0,.055,1.85),(4.97,.02,3.68),edge,.009)
|
||||
cube('Information substrate',(0,.025,1.86),(4.80,.012,3.47),diffuser,.006)
|
||||
for z in [.028,3.672]:cube('Polished perimeter rail',(0,-.021,z),(4.95,.155,.034),edge,.009)
|
||||
for x in [-2.476,2.476]:cube('Polished perimeter rail',(x,-.021,1.85),(.034,.155,3.66),edge,.009)
|
||||
# Wide optical cavities sit BEHIND the frosted cover. Their lenticular profiles
|
||||
# are shallow; no torus protrudes from the exterior face.
|
||||
for x,z,r in [(-.44,1.92,.79),(1.13,2.48,.435)]:
|
||||
width=.145 if r>.5 else .102
|
||||
annular_profile('Embedded optical cavity',x,z,[
|
||||
(r-width,.016),(r+width,.016),(r+width+.012,.003),
|
||||
(r+width,-.014),(r+width-.022,-.027),(r+.032,-.040),
|
||||
(r-.012,-.039),(r-.045,-.052),(r-width+.025,-.054),
|
||||
(r-width,-.036)],optics)
|
||||
annular_profile('Subsurface refractive shoulder',x,z,[
|
||||
(r+.028,-.031),(r+width+.025,-.009),(r+width+.029,-.020),
|
||||
(r+width+.012,-.032),(r+.056,-.053),(r+.028,-.049)],optical_edge)
|
||||
annular_profile('Inner optical bevel',x,z,[
|
||||
(r-width-.012,-.017),(r-width+.036,-.041),
|
||||
(r-width+.041,-.057),(r-width+.023,-.064),
|
||||
(r-width+.004,-.061),(r-width-.012,-.036)],optical_edge)
|
||||
for offset,tube,y in [(width+.009,.006,-.031),(.030,.007,-.054),(-width+.022,.006,-.064)]:
|
||||
o=torus('Concentric optical machining',x,z,r+offset,tube,optical_edge,y)
|
||||
o.scale.z=.42
|
||||
if r<.5:
|
||||
annular_profile('Embedded amber annulus',x,z,[
|
||||
(r-.170,-.047),(r-.070,-.047),(r-.067,-.060),
|
||||
(r-.077,-.071),(r-.156,-.071),(r-.170,-.060)],gold)
|
||||
cube('Serial label',(-1.36,-.099,3.04),(.99,.02,.41),paper,.003)
|
||||
cube('Label top rule',(-1.36,-.116,3.23),(.98,.004,.008),ink,0)
|
||||
cube('Label bottom rule',(-1.36,-.116,2.847),(.98,.004,.005),ink,0)
|
||||
text('Company label','RHINE LAB, LLC.',-1.825,3.105,.105)
|
||||
text('Database label','INTERNAL DATABASE',-1.825,3.017,.042,core)
|
||||
text('Serial number','NO.001',-1.825,2.875,.148)
|
||||
text('Information label','INFO',-.99,3.075,.076)
|
||||
text('Symbol label','+ / -',-.99,2.9,.095)
|
||||
for i in range(16):
|
||||
o=cube('Laser etched vent',(1.04+i*.054,-.111,.57),(.023,.009,.1),core,.003);o.rotation_euler.y=.4
|
||||
for i in range(25):cube('Calibration mark',(-2.23,-.108,.61+i*.052),(.035 if i%5 else .075,.005,.006),core,0)
|
||||
text('Edge inscription','R H I N E L A B',-1.81,.28,.063,core)
|
||||
for z,x1,x2 in [(3.36,-1.8,.2),(3.36,.4,1.55),(.4,-1.45,1.75)]:
|
||||
cube('Engraved circuit trace',((x1+x2)/2,-.108,z),(x2-x1,.004,.005),core,.002)
|
||||
# Two sides of a shallow pressed channel, observed in the 37–39 second close-up.
|
||||
# Keep the entire channel behind the front cover to avoid coplanar stippling.
|
||||
top=[(-2.27,3.06),(-2.27,3.30),(-2.10,3.45),(-1.72,3.45),(-1.61,3.51),
|
||||
(-.65,3.51),(-.55,3.46),(.37,3.46),(.43,3.52),(.49,3.46),
|
||||
(1.08,3.46),(1.17,3.52),(1.20,3.50),(1.13,3.42),
|
||||
(1.79,3.42),(1.89,3.51),(2.19,3.51),(2.29,3.41),(2.29,3.03)]
|
||||
channel('Moulded circuit channel shadow',top,-.071,.005,core)
|
||||
channel('Moulded circuit channel lip',[(x,z-.018) for x,z in top],-.075,.008,optical_edge)
|
||||
perimeter=[(-2.18,2.85),(-2.23,2.72),(-2.23,.43),(-2.12,.30),
|
||||
(2.08,.30),(2.24,.44),(2.24,2.97)]
|
||||
channel('Moulded inner perimeter',perimeter,-.067,.008,optical_edge)
|
||||
for z in [.080,3.620]:
|
||||
cube('Carrier mating seam',(0,.002,z),(4.82,.012,.010),optical_edge,.003)
|
||||
for x in [-2.420,2.420]:
|
||||
cube('Carrier mating seam',(x,.002,1.85),(.010,.012,3.54),optical_edge,.003)
|
||||
# Small raised pads under the diagonal calibration vents.
|
||||
for i in range(16):
|
||||
cube('Moulded vent footing',(1.04+i*.054,-.071,.435),(.015,.014,.018),optical_edge,.004)
|
||||
|
||||
detail_script=Path(ROOT)/'art/clear_reference_details.py'
|
||||
exec(compile(detail_script.read_text(encoding='utf-8-sig'),str(detail_script),'exec'))
|
||||
|
||||
# Convert text, bake modifiers, and group by material for efficient instancing.
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
for o in list(scene.objects):
|
||||
bpy.context.view_layer.objects.active=o
|
||||
if o.type in ['FONT','CURVE']:bpy.ops.object.convert(target='MESH')
|
||||
for m in list(o.modifiers):
|
||||
try:bpy.ops.object.modifier_apply(modifier=m.name)
|
||||
except:pass
|
||||
for mat in list(dict.fromkeys(o.data.materials[0] for o in scene.objects if o.type=='MESH' and o.data.materials)):
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obs=[o for o in scene.objects if o.type=='MESH' and o.data.materials and o.data.materials[0]==mat]
|
||||
if not obs:continue
|
||||
for o in obs:o.select_set(True)
|
||||
bpy.context.view_layer.objects.active=obs[0];bpy.ops.object.join()
|
||||
o=bpy.context.object;o.name=mat.name
|
||||
scene.cursor.location=(0,0,0);bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
|
||||
# Bake orientation to make every exported group share the same coordinate frame.
|
||||
bpy.ops.object.transform_apply(location=False,rotation=True,scale=True)
|
||||
# Annotated reference: the visible end face occupies roughly half a row pitch.
|
||||
for obj in scene.objects:
|
||||
if obj.type=='MESH':
|
||||
if obj.data.materials[0].name.startswith(('Optical_Glass_', 'Optical_Bridge_Glass', 'Amber_Optical_Inlay')):
|
||||
bpy.context.view_layer.objects.active=obj
|
||||
bpy.ops.object.select_all(action='DESELECT');obj.select_set(True)
|
||||
obj.scale.y *= 2.0
|
||||
bpy.ops.object.transform_apply(location=False,rotation=False,scale=True)
|
||||
else:
|
||||
for vertex in obj.data.vertices:vertex.co.y *= 2.0
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
export_path=Path(ROOT)/'art/.cache/archive-cassette.glb'
|
||||
export_path.parent.mkdir(parents=True,exist_ok=True)
|
||||
bpy.ops.export_scene.gltf(filepath=str(export_path),export_format='GLB',use_selection=True,use_active_scene=True,export_apply=True)
|
||||
os.replace(str(export_path),ROOT+'/public/assets/archive-cassette.glb')
|
||||
bpy.data.libraries.write(ROOT+'/art/rhine-archive.blend', {scene}, fake_user=True)
|
||||
print('Exported archive cassette:',len(scene.objects),'material groups')
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import bpy
|
||||
from pathlib import Path
|
||||
BUILD_ROOT = Path(__file__).resolve().parents[1]
|
||||
ROOT = BUILD_ROOT
|
||||
source = (ROOT/'art/build_archive.py').read_text(encoding='utf-8')
|
||||
prefix = source.split('# Convert text, bake modifiers')[0]
|
||||
prefix = prefix.replace("scene = bpy.data.scenes.new('Rhine_Archive_Work')", "scene = bpy.data.scenes.new('Rhine_Assembly_Work')")
|
||||
prefix = prefix.replace("if old != scene and old.name.startswith('Rhine_Archive_Asset'):", "if False:")
|
||||
prefix = prefix.replace("scene.name='Rhine_Archive_Asset'", "scene.name='Rhine_Assembly_Asset'")
|
||||
exec(compile(prefix, str(ROOT/'art/build_archive.py'), 'exec'))
|
||||
ROOT = BUILD_ROOT
|
||||
def part_for(name):
|
||||
if name.startswith(('Rear translucent carrier', 'Polished perimeter rail', 'Ivory spine cap', 'Carrier mating seam')):
|
||||
return 'carrier'
|
||||
if name.startswith('Information substrate'): return 'substrate'
|
||||
if name.startswith(('Embedded optical cavity', 'Embedded amber annulus', 'Folded optical tab')): return 'optical-core'
|
||||
if name.startswith(('Subsurface refractive shoulder', 'Inner optical bevel', 'Concentric optical machining', 'Optical ribbon')):
|
||||
return 'optical-lenses'
|
||||
if name.startswith(('Countersunk washer', 'Machined screw', 'Screw slot')): return 'fasteners'
|
||||
return 'cover'
|
||||
for obj in list(scene.objects):
|
||||
obj['assemblyPart'] = part_for(obj.name)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
if obj.type in ['FONT','CURVE']: bpy.ops.object.convert(target='MESH')
|
||||
for mod in list(obj.modifiers):
|
||||
bpy.ops.object.modifier_apply(modifier=mod.name)
|
||||
if obj.data.materials[0].name.split('.')[0] == 'Carbon_Ink':
|
||||
bpy.data.objects.remove(obj, do_unlink=True)
|
||||
pairs = {}
|
||||
for obj in scene.objects:
|
||||
key = (obj['assemblyPart'], obj.data.materials[0].name)
|
||||
pairs.setdefault(key, []).append(obj)
|
||||
for (part, surface), objects in pairs.items():
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
for obj in objects: obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = objects[0]
|
||||
bpy.ops.object.join()
|
||||
obj = bpy.context.object
|
||||
obj.name = part+'__'+surface.split('.')[0]
|
||||
obj['assemblyPart'] = part
|
||||
scene.cursor.location = (0,0,0)
|
||||
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
|
||||
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
||||
if surface.startswith(('Optical_Glass_', 'Optical_Bridge_Glass', 'Amber_Optical_Inlay')):
|
||||
obj.scale.y *= 2.0
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
else:
|
||||
for vertex in obj.data.vertices:vertex.co.y *= 2.0
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
export_path=ROOT/'art/.cache/archive-assembly.glb'
|
||||
export_path.parent.mkdir(parents=True,exist_ok=True)
|
||||
bpy.ops.export_scene.gltf(filepath=str(export_path), export_format='GLB', use_selection=True, use_active_scene=True, export_apply=True, export_extras=True)
|
||||
os.replace(str(export_path),str(ROOT/'public/assets/archive-assembly.glb'))
|
||||
bpy.data.libraries.write(str(ROOT/'art/archive-assembly.blend'), {scene}, fake_user=True)
|
||||
print('Assembly exported:', len(scene.objects), 'meshes, parts:', sorted({o['assemblyPart'] for o in scene.objects}))
|
||||
@@ -0,0 +1,47 @@
|
||||
# Detail reconstruction from the clear 40–42 second reference.
|
||||
# Executed by build_archive.py in the same scene; assembly uses this identical source.
|
||||
# All dimensions below are BEFORE the shared x2 thickness bake.
|
||||
from pathlib import Path
|
||||
|
||||
obsolete=('Embedded optical cavity','Subsurface refractive shoulder','Inner optical bevel',
|
||||
'Concentric optical machining','Embedded amber annulus','Moulded circuit channel',
|
||||
'Moulded inner perimeter','Moulded vent footing','Laser etched vent',
|
||||
'Calibration mark','Edge inscription','Engraved circuit trace')
|
||||
for obj in list(scene.objects):
|
||||
if obj.name.startswith(obsolete): bpy.data.objects.remove(obj,do_unlink=True)
|
||||
|
||||
emboss=material('Moulded_Lettering',(.81,.786,.75),.24,.13)
|
||||
architecture_script=Path(ROOT)/'art/internal_architecture.py'
|
||||
exec(compile(architecture_script.read_text(encoding='utf-8'),str(architecture_script),'exec'))
|
||||
|
||||
# Rebuild the moulding from normalized positions on the complete 41.0-second face.
|
||||
def face_point(px,py):return ((px-432)/1022*5-2.5,(1018-py)/676*3.7)
|
||||
def mould(name,pixels,depth=-.074,radius=.004,mat=core):
|
||||
return channel(name,[face_point(x,y) for x,y in pixels],depth,radius,mat)
|
||||
|
||||
shell_script=Path(ROOT)/'art/shell_reference_details.py'
|
||||
exec(compile(shell_script.read_text(encoding='utf-8'),str(shell_script),'exec'))
|
||||
|
||||
# Fine, lightly recessed rectangular backing routes, beneath the ring structures.
|
||||
for route in [[(681,470),(681,899),(1061,899),(1061,821),(1310,821),(1310,464),(1028,464),(1028,406)]]:
|
||||
mould('Information substrate fine route',route,.014,.0018,core)
|
||||
|
||||
# Embossed vertical company inscription belongs to the inner cover, not the ink label.
|
||||
inscription=text('Moulded vertical lettering','RHINE LAB, LLC.',0,0,.155,emboss)
|
||||
# Text baseline follows Blender local X; rotate it down the left edge in the X/Z plane.
|
||||
inscription.location=(-2.27,-.078,2.29)
|
||||
inscription.rotation_euler=(math.pi/2,math.pi/2,0)
|
||||
inscription.data.extrude=.003;inscription.data.bevel_depth=.0015;inscription.data.bevel_resolution=1
|
||||
inscription.data.space_character=1.10
|
||||
# Two small registration pads under the floating tabs.
|
||||
for x in [-1.61,-1.45]:cube('Information substrate registration pad',(x,-.029,.53),(.014,.009,.038),gold,.002)
|
||||
|
||||
# Lower-right circular service detail with a slanted slotted rail, seen under haze.
|
||||
px,pz=face_point(1312,923)
|
||||
for radius,depth,mat in [(.145,-.070,optical_edge),(.120,-.065,core),(.083,-.072,optical_edge)]:
|
||||
torus('Moulded service socket',px,pz,radius,.0055,mat,depth).scale.z=.45
|
||||
annular_profile('Moulded service socket center',px,pz,[(.021,-.040),(.043,-.040),(.048,-.060),(.035,-.074),(.021,-.068)],gold,32)
|
||||
for i in range(11):
|
||||
x=1.03+i*.063
|
||||
channel('Moulded diagonal vent',[(x-.035,.38),(x+.030,.53)],-.075,.008,optical_edge)
|
||||
channel('Moulded diagonal vent recess',[(x-.029,.38),(x+.036,.53)],-.071,.003,core)
|
||||
@@ -0,0 +1,107 @@
|
||||
"""User-selected first internal model, restored from revision 89c7742.
|
||||
|
||||
Keep the current outer case and reveal animation. This shared Blender source
|
||||
restores the annular lenses, inner hubs, muted orange and paired ribbons.
|
||||
"""
|
||||
amber_inlay=material('Amber_Optical_Inlay',(.48,.18,.052),.32,.28)
|
||||
film=material('Optical_Film',(.79,.755,.712),.30,.06)
|
||||
film_edge=material('Optical_Film_Edge',(.57,.545,.515),.27,.20)
|
||||
clip_metal=material('Optical_Clips',(.34,.335,.32),.34,.55)
|
||||
|
||||
# Large annular volume: broad, segmented shallow chamber surrounding a narrow raised bore.
|
||||
# The previous uniformly thick concentric tubes hid the asymmetric breaks.
|
||||
bx,bz=-.425,1.80
|
||||
outer_profile=[(.625,.013),(.918,.013),(.950,.004),(.949,-.010),
|
||||
(.928,-.023),(.897,-.037),(.848,-.046),(.717,-.048),
|
||||
(.667,-.044),(.635,-.030)]
|
||||
for start,end in [(1,89.7),(90.3,177.5),(181,227.5),(229,308.7),(309.3,359.7)]:
|
||||
annular_profile('Embedded optical cavity segment',bx,bz,outer_profile,optics,128,
|
||||
math.radians(start),math.radians(end))
|
||||
# Each lip has its own depth so the cross-section stays readable when unassembled.
|
||||
annular_profile('Inner optical bevel hub',bx,bz,[
|
||||
(.501,-.020),(.614,-.020),(.635,-.033),(.632,-.057),
|
||||
(.615,-.069),(.547,-.071),(.512,-.056),(.501,-.040)],optical_edge)
|
||||
for start,end in [(2,177),(181,227),(230,359)]:
|
||||
annular_profile('Subsurface refractive shoulder sector',bx,bz,[
|
||||
(.670,-.031),(.929,-.010),(.948,-.017),(.942,-.026),
|
||||
(.914,-.034),(.867,-.043),(.731,-.054),(.673,-.049)],optical_edge,128,
|
||||
math.radians(start),math.radians(end))
|
||||
# Thin glazing lips and a partial inner copper track seen at the upper-left.
|
||||
for radius,depth in [(.943,-.029),(.650,-.054),(.526,-.064)]:
|
||||
annular_profile('Concentric optical machining lip',bx,bz,[
|
||||
(radius-.004,depth+.003),(radius+.004,depth+.003),
|
||||
(radius+.005,depth-.001),(radius,depth-.004),
|
||||
(radius-.004,depth-.002)],film_edge)
|
||||
annular_profile('Embedded amber annulus inner arc',bx,bz,[
|
||||
(.546,-.061),(.568,-.061),(.574,-.070),(.568,-.075),(.548,-.075)],amber_inlay,
|
||||
128,math.radians(109),math.radians(225))
|
||||
annular_profile('Embedded amber annulus outer arc',bx,bz,[
|
||||
(.910,.009),(.982,.009),(.986,-.004),(.978,-.011),(.953,-.020),(.922,-.017)],amber_inlay,
|
||||
128,math.radians(229),math.radians(364))
|
||||
# Radial joints cross the wide outer lens, staying under the cover.
|
||||
for angle in [90,103,179,228,260,309]:
|
||||
a=math.radians(angle)
|
||||
channel('Concentric optical machining radial seam',
|
||||
[(bx+r*math.cos(a),bz+r*math.sin(a)) for r in [.674,.74,.88,.937]],
|
||||
-.057,.0024,film_edge)
|
||||
|
||||
# Small annular volume: concentric clear shoulder, darker seat, flat amber annulus and hub.
|
||||
sx,sz=1.125,2.455
|
||||
annular_profile('Embedded optical cavity small',sx,sz,[
|
||||
(.220,.014),(.511,.014),(.555,.000),(.560,-.015),
|
||||
(.529,-.035),(.471,-.050),(.360,-.055),(.231,-.037)],optics)
|
||||
annular_profile('Subsurface refractive shoulder small',sx,sz,[
|
||||
(.388,-.034),(.536,-.013),(.573,-.017),(.579,-.030),
|
||||
(.553,-.047),(.502,-.059),(.424,-.063),(.386,-.051)],optical_edge)
|
||||
annular_profile('Embedded amber annulus small',sx,sz,[
|
||||
(.230,-.045),(.347,-.045),(.356,-.056),(.351,-.071),
|
||||
(.241,-.071),(.230,-.060)],amber_inlay)
|
||||
annular_profile('Inner optical bevel small hub',sx,sz,[
|
||||
(.170,-.041),(.226,-.041),(.235,-.054),(.231,-.069),
|
||||
(.184,-.069),(.172,-.057)],optical_edge)
|
||||
for radius in [.381,.478,.565]:
|
||||
annular_profile('Concentric optical machining small lip',sx,sz,[
|
||||
(radius-.003,-.057),(radius+.003,-.057),(radius+.004,-.062),
|
||||
(radius,-.064),(radius-.003,-.062)],film_edge)
|
||||
|
||||
# The reference shows two nearly clear ribbon-like spans. Their physical role is
|
||||
# unknown; recreate visible sheet/rim geometry without inventing a mechanism.
|
||||
def ribbon(name, controls, width, depth):
|
||||
points=[];verts=[];faces=[];segments=40
|
||||
for i in range(segments+1):
|
||||
t=i/segments;u=1-t
|
||||
x=sum(w*p[0] for w,p in zip([u**3,3*u*u*t,3*u*t*t,t**3],controls))
|
||||
z=sum(w*p[1] for w,p in zip([u**3,3*u*u*t,3*u*t*t,t**3],controls))
|
||||
dx=3*u*u*(controls[1][0]-controls[0][0])+6*u*t*(controls[2][0]-controls[1][0])+3*t*t*(controls[3][0]-controls[2][0])
|
||||
dz=3*u*u*(controls[1][1]-controls[0][1])+6*u*t*(controls[2][1]-controls[1][1])+3*t*t*(controls[3][1]-controls[2][1])
|
||||
length=math.hypot(dx,dz);nx=-dz/length;nz=dx/length
|
||||
points.append(((x-width*nx/2,z-width*nz/2),(x+width*nx/2,z+width*nz/2)))
|
||||
for yy in [depth-.002,depth+.002]:
|
||||
for sign in [-1,1]:verts.append((x+sign*width*nx/2,yy,z+sign*width*nz/2))
|
||||
for i in range(segments):
|
||||
a=i*4;b=(i+1)*4
|
||||
faces.extend([(a,b,b+1,a+1),(a+2,a+3,b+3,b+2),
|
||||
(a,a+2,b+2,b),(a+1,b+1,b+3,a+3)])
|
||||
faces.extend([(0,1,3,2),(segments*4,segments*4+2,segments*4+3,segments*4+1)])
|
||||
mesh=bpy.data.meshes.new(name);mesh.from_pydata(verts,[],faces);mesh.update()
|
||||
obj=bpy.data.objects.new(name,mesh);scene.collection.objects.link(obj);mesh.materials.append(film)
|
||||
for face in mesh.polygons:face.use_smooth=True
|
||||
for side in [0,1]:channel(name+' edge',[p[side] for p in points],depth-.003,.0018,film_edge)
|
||||
|
||||
ribbon('Optical ribbon inner',[(.31,1.16),(.99,1.33),(1.00,1.78),(.73,2.20)],.10,-.008)
|
||||
ribbon('Optical ribbon outer',[(.03,.94),(.97,1.13),(1.14,1.75),(.84,2.13)],.11,.007)
|
||||
channel('Optical ribbon tangent upper',[(-.67,2.73),(.98,3.01)],.003,.0018,film_edge)
|
||||
channel('Optical ribbon tangent lower',[(.06,.91),(1.44,2.03)],.006,.0018,film_edge)
|
||||
|
||||
# Omit the three tall structures to the left, as requested: their depth would
|
||||
# exceed the cassette. Use the 46–51 second views only for the annular buildings.
|
||||
# Closely spaced facade mullions sit under the roof edge, visible from oblique views.
|
||||
for cx,cz,r,count in [(bx,bz,.945,84),(sx,sz,.544,52)]:
|
||||
for i in range(count):
|
||||
angle=2*math.pi*i/count
|
||||
if cx==bx and 177<math.degrees(angle)<182:continue
|
||||
obj=cube('Embedded optical cavity facade mullion',
|
||||
(cx+r*math.cos(angle),-.011,cz+r*math.sin(angle)),(.009,.056,.0034),film_edge,.001)
|
||||
obj.rotation_euler.y=-angle
|
||||
annular_profile('Embedded amber annulus small lower fascia',sx,sz,[
|
||||
(.526,.010),(.557,.010),(.561,.001),(.558,-.009),(.532,-.009)],amber_inlay)
|
||||
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
from pathlib import Path
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
s=bpy.context.scene
|
||||
for obj in list(s.objects):
|
||||
if obj.type in ['CAMERA','LIGHT']:bpy.data.objects.remove(obj,do_unlink=True)
|
||||
bpy.ops.object.camera_add(location=(-7,-11,6))
|
||||
c=bpy.context.object;c.name='Asset_Review_Camera'
|
||||
c.rotation_euler=((Vector((0,0,1.9))-c.location).to_track_quat('-Z','Y').to_euler())
|
||||
c.data.type='ORTHO';c.data.ortho_scale=7.3;s.camera=c
|
||||
s.world=bpy.data.worlds.new('Warm_Studio_World');s.world.use_nodes=True
|
||||
s.world.node_tree.nodes['Background'].inputs[0].default_value=(.8,.77,.72,1)
|
||||
s.world.node_tree.nodes['Background'].inputs[1].default_value=.65
|
||||
for name,loc,power,size in [('Softbox_Key',(-4,-5,8),950,7),('Softbox_Rim',(5,1,6),1100,5),('Softbox_Fill',(1,-7,3),350,6)]:
|
||||
bpy.ops.object.light_add(type='AREA',location=loc)
|
||||
light=bpy.context.object;light.name=name;light.data.energy=power;light.data.shape='DISK';light.data.size=size
|
||||
light.rotation_euler=((Vector((0,0,2))-light.location).to_track_quat('-Z','Y').to_euler())
|
||||
s.render.engine='CYCLES';s.cycles.samples=48;s.cycles.use_denoising=True
|
||||
s.render.resolution_x=1200;s.render.resolution_y=1000;s.render.resolution_percentage=100
|
||||
s.render.film_transparent=False;s.render.filepath=str(ROOT/'art/archive-studio.png')
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(ROOT/'art/rhine-archive.blend'))
|
||||
@@ -0,0 +1,63 @@
|
||||
# Reference face: x=24..1021, y=6..667. Shared by both Blender exports.
|
||||
# The engraved frame is below the cover, so the moving frost masks every route.
|
||||
engraving=material('Case_Engraving',(.57,.54,.50),.39,.12)
|
||||
lip=material('Case_Engraving_Highlight',(.90,.87,.83),.25,.16)
|
||||
inlay=material('Index_Inlay',(.67,.53,.40),.52,.04)
|
||||
def case_point(px,py):return ((px-24)/997*5-2.5,(667-py)/661*3.7)
|
||||
def route(name,pixels):
|
||||
points=[case_point(x,y) for x,y in pixels]
|
||||
channel('Case engraved '+name,points,-.075,.0045,engraving)
|
||||
channel('Case pressed lip '+name,[(x+.007,z-.009) for x,z in points],-.079,.004,lip)
|
||||
|
||||
# The top patch finishes at the case's top and front planes: no raised block.
|
||||
x,z=case_point(92,26)
|
||||
cut=cube('Temporary inlay recess',(x,-.100,z),(.25,.030,2*(3.7-z)+.002),core,0)
|
||||
bpy.context.view_layer.objects.active=front_cover
|
||||
mod=front_cover.modifiers.new('Flush index pocket','BOOLEAN');mod.operation='DIFFERENCE';mod.object=cut
|
||||
bpy.ops.object.modifier_apply(modifier=mod.name);bpy.data.objects.remove(cut,do_unlink=True)
|
||||
cube('Index flush inlay',(x,-.102,z),(.25,.002,2*(3.7-z)),inlay,.0008)
|
||||
|
||||
route('outer upper left',[(974,14),(35,14),(35,612)])
|
||||
route('outer lower right',[(1011,64),(1011,655),(80,655)])
|
||||
route('upper circuit',[(35,171),(54,171),(78,143),(78,76),(98,57),(172,57),
|
||||
(191,46),(383,46),(405,57),(598,57),(617,68),(650,68),(655,64),(650,57),
|
||||
(620,57),(605,44),(600,43),(596,47),(597,54),(614,67),(760,67),
|
||||
(779,53),(782,47),(778,43),(773,44),(757,57),(722,57),(719,61),(720,68),
|
||||
(763,68),(780,57),(894,57),(917,40),(973,40)])
|
||||
route('left hook',[(68,47),(51,64),(51,105),(53,111),(60,113),(64,109),
|
||||
(64,70),(90,42),(128,42),(134,39),(135,34),(131,28),(119,28)])
|
||||
route('inner frame',[(115,565),(115,84),(132,67),(948,67),(968,85),(968,162),
|
||||
(981,192),(981,550),(909,622),(131,622),(118,609),(118,590)])
|
||||
route('lower return',[(128,629),(911,629),(985,556)])
|
||||
route('bottom catch',[(793,652),(798,648),(890,648),(894,653),(1010,653)])
|
||||
|
||||
for label,px,py,start,end in [('upper right',999,37,0.30*math.pi,1.84*math.pi),
|
||||
('lower left',57,642,-.75*math.pi,.78*math.pi)]:
|
||||
x,z=case_point(px,py)
|
||||
# C-shaped shallow boss, tied into the pressed frame rather than a raised ring.
|
||||
for radius in [.104,.127]:
|
||||
points=[(x+radius*math.cos(start+(end-start)*i/64),
|
||||
z+radius*math.sin(start+(end-start)*i/64)) for i in range(65)]
|
||||
channel('Case engraved screw boss '+label,points,-.076,.0045,engraving)
|
||||
channel('Case pressed screw boss '+label,[(a+.006,b-.008) for a,b in points],-.080,.004,lip)
|
||||
# A real opening leaves the flush metal head readable under a frosted cover.
|
||||
bpy.ops.mesh.primitive_cylinder_add(vertices=64,radius=.082,depth=.06,
|
||||
location=(x,-.095,z),rotation=(math.pi/2,0,0))
|
||||
cut=bpy.context.object
|
||||
bpy.context.view_layer.objects.active=front_cover
|
||||
mod=front_cover.modifiers.new('Flush fastener opening','BOOLEAN');mod.operation='DIFFERENCE';mod.object=cut
|
||||
bpy.ops.object.modifier_apply(modifier=mod.name);bpy.data.objects.remove(cut,do_unlink=True)
|
||||
annular_profile('Countersunk washer '+label,x,z,
|
||||
[(.061,-.094),(.080,-.094),(.080,-.100),(.071,-.103),(.063,-.103),(.061,-.099)],lip,64)
|
||||
bpy.ops.mesh.primitive_cylinder_add(vertices=48,radius=.060,depth=.010,
|
||||
location=(x,-.098,z),rotation=(math.pi/2,0,0))
|
||||
head=bpy.context.object;head.name='Machined screw '+label;head.data.materials.append(metal)
|
||||
mod=head.modifiers.new('Head chamfer','BEVEL');mod.width=.003;mod.segments=3
|
||||
bpy.ops.object.modifier_apply(modifier=mod.name)
|
||||
for angle in [-.22,math.pi/2-.22]:
|
||||
cut=cube('Temporary drive cutter',(x,-.103,z),(.069,.008,.014),core,.001)
|
||||
cut.rotation_euler.y=angle
|
||||
bpy.context.view_layer.objects.active=head
|
||||
mod=head.modifiers.new('Cross recessed drive','BOOLEAN');mod.operation='DIFFERENCE';mod.object=cut
|
||||
bpy.ops.object.modifier_apply(modifier=mod.name);bpy.data.objects.remove(cut,do_unlink=True)
|
||||
head.modifiers.new('Fastener normals','WEIGHTED_NORMAL')
|
||||
@@ -0,0 +1,22 @@
|
||||
# 真实文件夹与玻璃档案交互
|
||||
|
||||
2026-09-10 用户提出:将当前阵列交互迁移到桌面真实文件夹。当前阶段为可行性评估,没有安装额外服务或读取用户目录。
|
||||
|
||||
## 结论
|
||||
|
||||
现有 Three.js 模型与拖动、波浪、抽取交互可以复用。若需要展示普通文件夹的内容,并由系统默认程序打开文件,建议网页壁纸配合一个本地程序;或将前端装入桌面应用容器。当前网页壁纸公开接口不足以单独承诺完整功能。
|
||||
|
||||
Wallpaper Engine 的网页 directory 属性支持图片、视频目录,不等于通用文件系统浏览。官方 openUserShortcut 文档属于 SceneScript,文档明确只允许在鼠标事件中调用,不能直接当成网页 JavaScript 接口使用。
|
||||
|
||||
## 推荐的后续小范围试验
|
||||
|
||||
由用户选择一个根目录;每列代表子文件夹、每张玻璃档案代表文件。选中显示名称、类型、大小和修改日期,点击“打开”调用系统默认程序,另提供“在资源管理器中显示”。缺失文件有明确提示,目录改动后刷新。初版不包含删除、移动、重命名,也不要求接管系统桌面图标。
|
||||
|
||||
本地程序只监听回环地址,用每次启动的访问凭据限制网页访问;只允许用户选定根目录下的文件,校验解析后的路径及链接目标,不接受任意命令行。网页发送文件标识,由本地程序解析已登记路径后打开。配套程序需要安装/启动/退出入口;发布时必须明确它是额外组件。
|
||||
|
||||
独立桌面应用能更完整地支持键盘、文件拖放和系统对话框,但嵌入桌面背景、多显示器与桌面图标的关系需要单独实机验证。不能把普通透明窗口称作已完成桌面集成。
|
||||
|
||||
## 官方依据
|
||||
|
||||
- 网页用户属性与目录范围:https://docs.wallpaperengine.io/en/web/customization/properties.html
|
||||
- SceneScript 用户快捷方式:https://docs.wallpaperengine.io/en/scene/userproperties/usershortcut.html
|
||||
@@ -0,0 +1,146 @@
|
||||
# 本地归档接入(LOCAL ARCHIVE)
|
||||
|
||||
把 `E:\归档` 接进本界面:主阵列展示从归档中精选的四十条档案,**LOCAL ARCHIVE** 面板负责检索归档的全部条目并直接执行归档写操作。
|
||||
|
||||
## 两块数据的关系
|
||||
|
||||
| | 三维阵列 | 主检索的本地归档栏 + LOCAL ARCHIVE 面板 |
|
||||
| --- | --- | --- |
|
||||
| 数据 | `public/archive-index.json`(全量,运行时载入) | 同一份索引 |
|
||||
| 内容 | 列 = 归档顶层内容分类,列内 = 该分类的**全部真实归档条目** | 全量条目(约 5 万条路径) |
|
||||
| 用途 | 三维阵列、详情、解密动效、抽盘与拖拽 | 列表检索、打开、定位、归档操作 |
|
||||
| 条数约束 | 无(随归档变化) | 无 |
|
||||
| 降级 | 索引不可用时退回 `content/archives.json` 的四十条精选档案 | 退回静态快照,只读 |
|
||||
|
||||
**2026-09-12 起阵列已改为全量归档驱动**,因此归档里新增的文件会直接出现在阵列中(换到它所在的列即可)。三维阵列与检索列表共用同一份内存索引(`loadArchiveIndex`),不会重复下载。
|
||||
|
||||
四十条精选档案现在只作为降级数据集与 TXT 摘要来源,见 [`../content/README.md`](../content/README.md)。全量阵列的设计、性能与证据见 [`../verification/ARCHIVE-ARRAY.md`](../verification/ARCHIVE-ARRAY.md)。
|
||||
|
||||
|
||||
|
||||
## 启动
|
||||
|
||||
推荐一条命令同时拉起归档服务与开发服务器:
|
||||
|
||||
```bash
|
||||
npm run dev:archive
|
||||
```
|
||||
|
||||
也可以分开启动(两个终端):
|
||||
|
||||
```bash
|
||||
npm run archive:serve # 归档服务,默认 127.0.0.1:43117
|
||||
npm run dev # Vite 开发服务器
|
||||
```
|
||||
|
||||
只跑 `npm run dev` 也能用:面板会退回**只读检索模式**,此时打开文件、定位与归档操作会被禁用并给出提示。
|
||||
|
||||
启动后点击顶部导航的 **LOCAL ARCHIVE**。
|
||||
|
||||
## 数据更新
|
||||
|
||||
服务启动时会扫描一次归档并在 `E:\归档` 上挂一个递归监视器。归档里新增或改动文件后,约 2.5 秒防抖,服务自动重新生成快照,并通过事件流通知已打开的页面重新载入索引——不需要手动刷新。
|
||||
|
||||
也可以点面板状态栏的**重新扫描 ↻**,或调用 `POST /archive-api/refresh`。
|
||||
|
||||
`npm run dev` / `npm run build` 的钩子也会各生成一次快照。
|
||||
|
||||
## 详情页的「文件位置」按钮
|
||||
|
||||
详情页右下角的 **SHOW IN FOLDER / 文件位置** 按钮(原先是 EXPORT 下载 TXT)会直接在资源管理器中定位当前档案在 `E:\归档` 中的位置,用的是该条精选档案在 `content/archives.json` 里的真实 `path`,通过本地归档服务的 `POST /reveal` 完成。
|
||||
|
||||
- 服务未运行时按钮会提示"需要本地归档服务:请运行 `npm run dev:archive`",不会静默失败。
|
||||
- 详情页脚注显示的是归档内的相对路径文本。浏览器不允许网页跳转 `file:` 链接,所以它不再是可点击链接。
|
||||
- `public/archives/*.txt` 仍会为每条档案生成,但详情页不再提供下载入口。
|
||||
|
||||
## 面板能做什么
|
||||
|
||||
**检索**:输入任意片段,匹配相对路径(含文件名与扩展名)。命中数实时显示,单次最多渲染 200 行,匹配片段高亮。索引在第一次打开面板时载入一次,之后在内存中过滤,不再走网络。
|
||||
|
||||
**每行的三个动作**:
|
||||
|
||||
| 按钮 | 行为 | 是否需要服务 |
|
||||
| --- | --- | --- |
|
||||
| 打开 | 用系统默认程序打开文件;目录则用资源管理器打开 | 是 |
|
||||
| 定位 | 在资源管理器中选中该条目 | 是 |
|
||||
| 归档… | 选中该条目作为源,在右侧进入归档操作 | 是 |
|
||||
|
||||
**归档操作(四种,均为安全写)**:
|
||||
|
||||
| 操作 | 说明 |
|
||||
| --- | --- |
|
||||
| 移动到 | 归档内移动,同盘重命名。**跨盘拒绝**,请改用复制 |
|
||||
| 复制到 | 递归复制 |
|
||||
| 解压到 | 调用 `C:\Program Files\7-Zip\7z.exe` 解压到目标目录 |
|
||||
| 新建分类目录 | 在归档内新建目录 |
|
||||
|
||||
一律**先点「预览影响」**:显示源、目标、体积、文件数、是否跨盘,并给出能不能执行的结论。`预览`不通过时执行按钮保持禁用。执行结果用界面右下角的提示条反馈,并追加到操作日志。
|
||||
|
||||
**不提供删除与重命名**:这两项不可逆,需要时请在资源管理器中手动完成。
|
||||
|
||||
## 安全设计
|
||||
|
||||
- **只监听回环**:服务绑定 `127.0.0.1`,不对外网开放。
|
||||
- **每次启动的访问令牌**:服务启动时生成随机令牌写入项目根目录的 `.archive-token`(已加入 `.gitignore`)。浏览器**不持有**令牌——Vite 在 `/archive-api` 代理层注入它。因此即使别的网页知道端口号,也无法调用本服务。
|
||||
- **路径围栏**:客户端只传相对路径,服务解析为绝对路径后必须仍落在归档根目录内;越界、绝对路径与 `..` 一律 `403`。符号链接不算越界,但不会被跟随。
|
||||
- **操作日志**:每次写操作追加一行 JSON 到 `E:\归档\00_索引\操作日志.jsonl`,含时间、操作、源、目标。
|
||||
- **写操作需要显式确认**:`/apply` 必须带 `confirm: true`,否则 `400`。服务端会**重新计算**一次影响预览,不信任客户端传来的结论。
|
||||
|
||||
## 配置
|
||||
|
||||
[`archive.config.json`](../archive.config.json):
|
||||
|
||||
```json
|
||||
{
|
||||
"root": "E:\\归档",
|
||||
"port": 43117,
|
||||
"exclude": [],
|
||||
"indexFile": "public/archive-index.json",
|
||||
"logFile": "00_索引\\操作日志.jsonl"
|
||||
}
|
||||
```
|
||||
|
||||
`exclude` 里的目录会被索引跳过(其本身仍作为目录条目保留)。改 `port` 时不需要同步改前端:Vite 代理会在配置加载时读取同一个文件。
|
||||
|
||||
## 接口
|
||||
|
||||
所有接口都需要 `x-archive-token` 头,经 Vite 代理访问时自动注入。在浏览器或 curl 里直接调用时前缀为 `/archive-api`,直连服务时无前缀。
|
||||
|
||||
| 方法与路径 | 用途 |
|
||||
| --- | --- |
|
||||
| `GET /status` | 根目录、端口、监视状态、最近一次快照时间、总量 |
|
||||
| `GET /index` | 全量索引(支持 gzip,约 0.7 MB) |
|
||||
| `GET /search?q=&limit=` | 服务端检索 |
|
||||
| `POST /plan` | 影响预览,不修改任何文件 |
|
||||
| `POST /apply` | 执行写操作,需要 `confirm: true` |
|
||||
| `POST /open` / `POST /reveal` | 打开文件 / 在资源管理器中定位 |
|
||||
| `POST /refresh` | 立即重新扫描归档 |
|
||||
| `GET /events` | 快照更新事件流(SSE) |
|
||||
|
||||
## 快照文件
|
||||
|
||||
| 文件 | 体积 | 说明 |
|
||||
| --- | ---: | --- |
|
||||
| `content/archives.json` | 约 45 KB | 四十条精选档案,页面与 TXT 下载共用 |
|
||||
| `public/archive-index.json` | 约 7.4 MB | 全量索引,紧凑数组 `[路径, 类型, 字节, mtime]` |
|
||||
| `public/archives/*.txt` | 40 个 | 每条档案的可下载摘要 |
|
||||
|
||||
`archive-index.json` **不进入 PWA 离线预缓存**(`scripts/build-pwa.mjs` 的白名单不含根目录文件),因此离线包体积不受影响;代价是离线状态下打开检索面板只有空态。服务在线时索引走 gzip(约 0.7 MB),比静态快照快得多。
|
||||
|
||||
## 构建与部署
|
||||
|
||||
生产构建不包含归档服务,也不带 `/archive-api` 代理,线上站点打开面板会显示"只读检索模式",检索走静态 `archive-index.json`,写操作不可用——这是刻意的:本地归档服务不应暴露在公网。
|
||||
|
||||
`npm run preview` 同样配置了代理,因此本地预览可以完整体验。
|
||||
|
||||
## 故障排查
|
||||
|
||||
| 现象 | 处理 |
|
||||
| --- | --- |
|
||||
| 面板显示"服务未运行" | 先 `npm run archive:serve`;确认端口 43117 未被占用 |
|
||||
| 打开/定位提示需要服务 | 同上;这是刻意的降级,检索仍可用 |
|
||||
| 令牌无效(401) | 服务重启会换新令牌;重启 Vite 让代理读到新值,或直接用 `npm run dev:archive` |
|
||||
| 「7-Zip 解压失败」 | 确认 `C:\Program Files\7-Zip\7z.exe` 存在;面板 `/status` 的 `sevenZip` 字段会说明 |
|
||||
| 新增文件没有出现在检索里 | ① 等约 3 秒(防抖),或点「重新扫描 ↻」② 查 `/status` 的 `lastError` 与 `lastScan` ③ 手动 `npm run archive:snapshot`。索引与检索本身极少出错,**先查服务和快照时间,再怀疑代码**。 |
|
||||
| 面板开了很久、刚加的文件搜不到 | 面板**每次打开都会重新取索引**(服务在线时),关闭面板不再退掉事件订阅;若仍不对,`Ctrl+F5` 整页刷新一次。 |
|
||||
| 摘要里的数字和归档对不上 | 重新生成快照;`{size}`/`{files}`/`{date}` 只在生成时采样 |
|
||||
@@ -0,0 +1,43 @@
|
||||
# 主屏幕安装与离线使用
|
||||
|
||||
正式地址:[rhine.lubeiluchen.cc](https://rhine.lubeiluchen.cc/)。
|
||||
|
||||
## iPhone / iPad
|
||||
|
||||
1. 用 Safari 打开正式地址。
|
||||
2. 轻点“分享”,选择“添加到主屏幕”;如果出现“作为网页 App 打开”,保持开启。
|
||||
3. 从主屏幕的莱茵生命图标打开,即可使用没有 Safari 地址栏的独立窗口。系统状态栏和底部手势区域会保留安全间距。
|
||||
|
||||
首次保持联网,在终端设置的“主屏幕与离线”区域看到“离线资源已就绪”后,即可断网重新打开。离线资源约 34 MiB,包含模型、完整字体分包、音乐与 40 份档案。普通页面按需加载字体,完整离线资源在进入终端后于后台准备。Safari 标签页和主屏幕 App 的存储可能各自独立,请以主屏幕 App 内显示的状态为准。
|
||||
|
||||
如果浏览器清除了网站数据或系统回收了缓存,需要联网重新准备离线资源。启用声音时,资源就绪后轻触“点击进入”(或按 Enter),声音准备完成后统一开始开机动画;关闭全部声音的用户直接进入。声音加载失败可重试,也可选择“关闭声音并进入”,此选择会关闭并保存音效与音乐开关。开启减少动态效果时仍可点击进入并播放声音,但会跳过开机动画。
|
||||
|
||||
## 桌面与其他手机
|
||||
|
||||
支持安装的浏览器会在终端设置中提供“安装到设备”,也可使用浏览器菜单安装。不同浏览器的菜单名称可能不同;普通浏览器标签页同样支持在线使用。
|
||||
|
||||
阵列上左右滑动切列,上下滑动切档;也可轻点方向按钮。详情中的正文独立滚动。360° 查看器支持单指旋转、双指缩放和平移,以及拆解、重组和复位。横竖屏切换保留当前档案和查看器状态。
|
||||
|
||||
## 更新
|
||||
|
||||
新版本会在后台下载完整资源,准备好后在页面底部提供“更新并重启”,也可从右上角“设置”中更新。更新不会清除收藏与偏好。下载失败时继续使用上一个完整离线版本,联网后重试即可。
|
||||
|
||||
如果旧版只有提示、找不到更新入口,请打开[更新终端](https://rhine.lubeiluchen.cc/update.html),点击“更新并返回”。这个入口从网络获取,不经过旧版本的页面缓存;应用完整新版后返回终端。普通“清除缓存”不一定清除 Service Worker 的离线副本,无需清空网站数据或收藏。
|
||||
|
||||
## 开机动画与选档动效
|
||||
|
||||
首次进入时,本站遵循系统的减少动画偏好。Win11 的“设置 → 辅助功能 → 视觉效果 → 动画效果”关闭时,Chrome / Edge 可能请求减少动画,本站会跳过开机动画并简化选档等动效。终端之后会保存本地偏好,资源更新不会重置它。
|
||||
|
||||
需要完整动效时,打开右上角圆形图标下标有“设置”的按钮,选择“启用完整动效并重播”。这只调整本站,无需修改系统设置。减少动态效果开关也可随时重新开启。
|
||||
|
||||
## 开发与 Vercel
|
||||
|
||||
`npm run build` 生成静态站点和带内容版本号的 Service Worker,输出在 `dist`。现有 Vercel 项目沿用 GitHub 自动部署,配置见 `vercel.json`。Service Worker、manifest 与构建清单使用重新验证缓存头。离线功能只在正式构建的 HTTPS 或 localhost 环境注册,`npm run dev` 不注册。
|
||||
|
||||
本地验证:运行 `npm run build`,再运行 `npm run preview`。浏览器测试见 `scripts/check-pwa.mjs`,需要本机可用的 Playwright 与 Chrome;可通过 `PLAYWRIGHT_MODULE` 指定已有 Playwright 模块路径。
|
||||
|
||||
`scripts/check-startup-motion.mjs` 验证系统偏好与本站覆盖、重播和正文解密;`scripts/check-pwa-recovery.mjs` 验证旧版迁移,需要以 `PWA_PREVIOUS_DIST` 指定保留的旧生产构建。两个脚本可设 `REVIEW_CHANNEL=msedge` 验证 Edge。更新恢复页保持网络获取,未加入离线资源清单。
|
||||
|
||||
图标源自项目共享莱茵生命 SVG 路径,生成脚本为 `scripts/build-icons.mjs`,通过 `SHARP_MODULE` 可指定本地 Sharp 模块。修改资源后重新构建即可生成新的离线版本,无需手动修改缓存编号。
|
||||
|
||||
平台说明参考:[WebKit 主屏幕 Web App](https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/)、[Safari 26 主屏幕安装行为](https://webkit.org/blog/17333/webkit-features-in-safari-26-0/)、[Service Worker 生命周期](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers)。
|
||||
@@ -0,0 +1,261 @@
|
||||
# Wallpaper Engine 本地实验
|
||||
|
||||
分支:`codex/wallpaper-engine`。2026-09-10 已在本机 Wallpaper Engine 的独立窗口中运行,尚未发布创意工坊、尚未应用到桌面,不合并主分支。
|
||||
|
||||
## 本机入口
|
||||
|
||||
Wallpaper Engine 本地库名称:**Rhine Lab · 莱茵生命档案终端(本地测试)**。
|
||||
|
||||
安装目录:`D:\Game\Steam\steamapps\common\wallpaper_engine\projects\myprojects\rhine-lab-local`。
|
||||
|
||||
当前预览窗口:**Rhine Lab - Wallpaper Test**。重新打开独立预览:
|
||||
|
||||
```powershell
|
||||
& 'D:\Game\Steam\steamapps\common\wallpaper_engine\wallpaper64.exe' -control openWallpaper -file 'D:\Game\Steam\steamapps\common\wallpaper_engine\projects\myprojects\rhine-lab-local\project.json' -playInWindow 'Rhine Lab - Wallpaper Test' -width 1600 -height 900
|
||||
```
|
||||
|
||||
项目目录中的 `project.json` 带有本地标题、预览图片和可调属性。只有构建产物目录应导入编辑器,不能导入整个源码仓库。重新发布未来已有的工坊项目时,应保留该项目的工坊 ID 与元数据。
|
||||
|
||||
## 构建与行为
|
||||
|
||||
```sh
|
||||
npm run build:wallpaper
|
||||
node scripts/check-wallpaper.mjs
|
||||
```
|
||||
|
||||
构建输出 `release/wallpaper`,约 34.1 MiB,823 个文件,包括完整字体分包、模型、音频、档案 TXT 和许可。字体和模型均本地读取,无需开发服务器或在线站点。源码、Blender 工程、参考视频、诊断服务不进入壁纸包。
|
||||
|
||||
- Vite 的 `wallpaper` 模式使用相对资源路径,HTML 中提前注册宿主回调,以免丢失模块载入前的初始属性。
|
||||
- 自动启动;宿主可以关闭开场,直接进入阵列。减少动态效果继续遵循显式设置。
|
||||
- 声音自动尝试解锁,等待最多 3 秒后继续画面;声音未能解锁时仍可通过后续点击触发。正常网页的点击启动流程保留。
|
||||
- 壁纸版关闭 PWA 初始化、安装和更新界面。更新通过替换本地构建或未来的工坊更新完成。
|
||||
- 宿主提供开场、音效、音乐、独立音量、减少动态效果和四档画质。宿主只推送变化的属性,页面保留其余设置。
|
||||
- 页面设置也可使用;重载时宿主下发的值具有优先权,页面内更改不会反向写回 Wallpaper Engine 的属性面板。
|
||||
- 三维渲染跟随宿主 FPS 上限(未收到时为 30),不会补渲染漏掉的帧。宿主暂停通知停止更新与音频;开场恢复时扣除暂停时间。
|
||||
- 壁纸版不提供浏览器全屏按钮。画质参数以点击循环选项的按钮呈现,保留原有参数值和高级设置。
|
||||
|
||||
## 本机验证记录
|
||||
|
||||
环境:Windows、本机 Wallpaper Engine 窗口显示版本 2.8.36,独立窗口 1600×900,真实 CEF / WebGL 渲染。管理界面另提示 2.8.42 已下载、待重启,本次没有执行更新。
|
||||
|
||||
已验证:
|
||||
|
||||
- 自动开场、白底 Logo 与文字、中文字体、三维阵列本地加载。
|
||||
- 鼠标点击切档(X-001 → X-006)、打开档案、磨砂揭示和正文解密。
|
||||
- 360° 模型本地加载、分层拆解与鼠标拖动旋转。
|
||||
- 初始宿主属性 `boot=false` 直接进入阵列,`musicvolume=15` 在页面显示 15%。
|
||||
- 运行中经官方 `applyProperties` 指令将音乐音量改为 23%,收到 `{musicvolume:{value:23}}` 回调,已打开的页面控件同步至 23%。
|
||||
- 画质按钮从“原始”切换为“高”,实际渲染从 1600×900 变为 2000×1125,并启用 SMAA。
|
||||
- 实际音频状态为 `running`,三条音乐轨道已载入并播放,错误字段为空。声音主观听感仍由用户试听。
|
||||
- 限帧、暂停恢复及部分属性回调通过脚本验证;新增回归检查确保壁纸不再生成原生 `<select>`。
|
||||
- `npm run build:wallpaper` 与正常网页 `npm run build` 均通过,网页 PWA 构建成功。
|
||||
|
||||
运行数据有一次阵列采样为 22 FPS(宿主上限 30),不能把上限写成稳定实际帧率。本次未做长时间功耗测试、4K / 多屏压力测试或真实桌面图标遮挡下的完整输入验证。
|
||||
|
||||

|
||||
|
||||
### 本机 CEF 的下拉框崩溃
|
||||
|
||||
打开原设置页会稳定出现 `STATUS_ILLEGAL_INSTRUCTION`。诊断确认页面逻辑完成后、首次绘制前崩溃;最小弹窗和声音控件正常,单独画质区域可复现,去掉其中 `<select>` 后正常。最终只在壁纸模式将画质下拉框换为循环按钮,恢复原背景模糊、焦点处理和原生音量滑块后,全设置页正常。
|
||||
|
||||
此记录描述本机的复现与兼容处理,不断言所有 CEF 版本都存在此问题。没有修改 Wallpaper Engine 全局画质、硬件加速或安全设置。
|
||||
|
||||
### 当前交付边界
|
||||
|
||||
本地壁纸库和独立预览可用;自动审批拒绝了替换主显示器壁纸,需用户明确批准后才应用到桌面。本次未上传创意工坊。上传前还应完成桌面输入、长时间性能和发布素材的最终检查。
|
||||
|
||||
## 官方接口参考
|
||||
|
||||
- [创建与导入网页壁纸](https://docs.wallpaperengine.io/en/web/first/gettingstarted.html)
|
||||
- [用户属性与暂停通知](https://docs.wallpaperengine.io/en/web/api/propertylistener.html)
|
||||
- [FPS 限制](https://docs.wallpaperengine.io/en/web/performance/fps.html)
|
||||
- [命令行预览与属性设置](https://help.wallpaperengine.io/en/functionality/cli.html)
|
||||
|
||||
## 桌面试用反馈与设置说明(2026-09-10)
|
||||
|
||||
用户已自行应用到桌面。设置顶部明确说明:每次启动读取 Wallpaper Engine 属性,页面内修改只对当前运行生效;长期设置须在宿主属性面板修改。画质区不再提示自动保存。
|
||||
|
||||
用户反馈桌面方向键与滚轮无效。代码检查确认两类监听仍保留,没有壁纸模式禁用分支;尚未通过桌面事件跟踪确认滚轮未传入的具体原因。设置操作说明改为拖动阵列、点击界面按钮,并注明桌面可能无法传入方向键和滚轮。Wallpaper Engine 开发者说明键盘输入存在宿主限制:https://steamcommunity.com/app/431960/discussions/2/1644304412654510366/ 。
|
||||
|
||||
本次 `npm run build:wallpaper` 与 `node scripts/check-wallpaper.mjs` 通过。
|
||||
|
||||
## 工作台首版
|
||||
|
||||
Wallpaper Engine 属性新增显示模式(桌面工作台/档案展示)、三条事项、重要日程名称和本地目标日期、专注与休息分钟数。页面设置可临时切换模式。空事项不会伪造数据。
|
||||
|
||||
工作台左侧始终显示本机时间、日期、事项。右侧五个功能入口与阵列五列联动。事项点击勾选或撤销;按本地日期自动重置。完成状态与计时存于独立 rhine-workbench-v1 项,宿主属性不覆盖它们。内容改动清除对应旧完成状态;存储不可用显示提示。该存储不承诺跨设备、换目录或卸载后保留。
|
||||
|
||||
专注与休息可开始、暂停、继续、重置、切换。截止时间采用 Date.now(),壁纸暂停、遮挡或重载后校正,不补响后台提醒,也不自动开始下一段。改变时长只影响下一次开始。媒体监听在经典启动脚本中提前注册,支持标题、歌手、封面、状态与可选进度;未启用、无曲目或无进度均有空状态。媒体来源取决于 Windows 媒体会话支持,模拟回调验证不代表所有播放器实测成功。
|
||||
|
||||
验证页:reference/workbench-review.html(开发 wallpaper 模式),包含仅用于测试的宿主属性与媒体回调。运行 node scripts/check-workbench.mjs 检查日期边界、暂停/重载计时和媒体切换残留。当前仍为本地实验,不合并正式网站。
|
||||
|
||||
2026-09-10 本次验证:壁纸与普通网页构建均通过;check-wallpaper 与 check-workbench 通过。在 Edge 实际页面验证设置切换、专注启动、事项勾选后重载保留、模拟媒体标题与进度、有效目标日期和无效日期提示,并检查桌面尺寸布局。媒体使用测试回调,未声称真实播放器联动已验证。本地安装目录已更新;本版尚待 Wallpaper Engine 桌面实际试听与存储复核。
|
||||
|
||||
2026-09-10 视觉修订:按用户要求撤回绿色主题,改用主页现有颜色,取消信息面板底块和导航色块,复用竖线刻度与黑白按钮状态。壁纸构建通过,并在 Edge 实际运行页面检查布局;已更新本地壁纸包。
|
||||
|
||||
## 工作台可配置显示(2026-09-10)
|
||||
|
||||
纯净效果是工作台的配置结果,不增加独立模式。在 WE 中选择“工作模式 → 桌面工作台”,按需切换“工作台:显示…”七个开关。全部关闭时仅保留三维阵列;设置按钮也可关闭,再从 WE 属性恢复。开关在启动时由宿主恢复,局部属性更新不会重置其他选项。壁纸内没有另一套临时显示开关。
|
||||
|
||||
工作台显示配置不影响档案展示。隐藏专注内容不停止正在运行的计时,也不清除事项完成状态。开场结束位置现在由下文的入场选项控制。
|
||||
|
||||
该阶段壁纸构建、check-wallpaper、check-workbench 通过。Edge 实际页面用宿主回调验证全部隐藏、只留时钟与外部恢复所有元素(包括设置入口),并检查纯净阵列截图。
|
||||
|
||||
## 音乐律动、波纹接力与入场(2026-09-10)
|
||||
|
||||
WE 属性使用六个原生可折叠 group:入场与画面、工作台界面显示、事项与日程、声音、音乐律动、波纹接力。工作模式始终在最上方。工作台分组仅在工作台模式显示;关闭声音、律动或游戏入口后隐藏对应音量、强度或节奏参数,保留已有值。使用本机 WE UI 实现中确认的 group 和 condition 字段,不伪造多层导航。
|
||||
|
||||
“入场时展示档案详情”提供随工作模式、展示详情、跳过详情三个选择。默认工作台在原片约 30.90 秒(内部时间 25.90)结束,阵列已进入并出现选择波浪,尚未进入完整抽取和特写;档案展示默认保留完整开场。工作台内容以 460ms 淡入和 9px 上移错峰显示。减少动态效果时直接显示。带 time 参数的逐帧对照仍保留完整原时间轴。
|
||||
|
||||
音乐律动只作用于工作台阵列;低、中、高频分别驱动宽波、小波与稀疏轻抬,保留原材质。默认开启、强度 100%,可设 0–200%。宿主没有信号或停止回调后缓慢回落。选中高度可设保持原样、仅音乐律动时归齐(默认)、始终归齐;归齐去掉选中隆起,不改变卡片朝向。减少动态效果时不播放频谱运动。默认在律动开启期间暂停壁纸自带配乐,避免内部配乐持续驱动频谱;此行为有独立开关,原配乐偏好不被改写。
|
||||
|
||||
波纹接力由左下角按钮开始,可在 WE 隐藏入口。点击抬起卡片或其编号得分,错误或超时结束,可重试或退出。三档节奏随得分渐快。游戏使用可见、射线可命中的真实卡片;暂时归齐阵列、淡出频谱、隐藏工作台文字并隔离浏览手势。退出恢复界面与律动。倒计时按实际经过时间运行,宿主暂停、后台或设置弹窗暂停计时,不因低帧率延长一轮。隐藏入口不会移除进行中游戏的退出按钮。
|
||||
|
||||
验证:check-playground 检查立体声频段、异常值、信号丢失回落、计分、重复点击、重试、超时、暂停、入场策略与六组属性条件;check-wallpaper、check-workbench 与两种构建继续运行。reference/playground-review.html 为不随安装包分发的宿主模拟面板,含三次接力后退出的集成验证。Edge 已观察真实模型抬起、三次得分后恢复、模拟音乐后归齐。系统实际音源与 WE 属性面板的最终显示仍需本机宿主复核,模拟频谱不代表真实音频已验证。
|
||||
|
||||
接口依据:[音频可视化](https://docs.wallpaperengine.io/en/web/audio/visualizer.html)、[分组](https://docs.wallpaperengine.io/en/scene/userproperties/overview.html)、[条件显示](https://docs.wallpaperengine.io/en/web/customization/displaycondition.html)。
|
||||
|
||||
## 登录入场适配与字幕移除
|
||||
|
||||
正常入场使用 openingLayout 铺满实际视口,中央二维动画保持原始局部坐标,背景和三维画布延展到屏幕边缘。窄屏缩小圆环的整体占用,竖屏提高验证文字字号;品牌文字保持边角锚定,小屏的进入按钮与页脚品牌分置左右。三维入场按实际宽高比修正镜头视野与目标投影,不移动档案来补构图。底部中文字幕节点和更新逻辑已移除。
|
||||
|
||||
显式 time / review 对照仍为 1920×1080 等比舞台,正常入场不再有固定比例留边。验证页 reference/opening-layout-review.html 提供 16:9、超宽屏、4:3、竖屏以及登录、圆环、欢迎、阵列节点。check-viewport 检查实际画布覆盖、中央布局边界、既有交互镜头和固定对照。浏览器尺寸模拟不代表手机实机性能数据。
|
||||
|
||||
最终检查:普通与壁纸构建通过,check-playground、check-wallpaper、check-workbench、check-viewport 通过。Edge 实际页面检查 960×540、1050×450、360×640 登录布局,以及 800×600 短入场结束后工作台可见、字幕节点不存在;小游戏入口隐藏后仍保留工作台。包已复制到本地 rhine-lab-local,安装入口哈希与构建一致,需宿主重新加载。保留为本地实验,不合并或上传。
|
||||
|
||||
## 明暗配色
|
||||
|
||||
在「入场与画面 → 界面配色」选择亮色或暗色;默认亮色。壁纸内部设置也可以即时试切,长期保存仍使用 WE 属性。配色覆盖工作台、档案详情、360° 查看器和开场。正常切换时卡片依次变色,减少动态效果时直接切换。重新载入壁纸后可见新增属性。
|
||||
|
||||
## 音乐无律动修复 · 2026-09-10
|
||||
|
||||
修正音频能力声明的位置:`supportsaudioprocessing` 必须放在 `general` 中,旧版误放在根层级。本机官方 Audiophile 示例和官方音频文档作为核对依据。构建脚本现在检查该路径,阻止错误配置发布;检查脚本同步修订,原有模拟回调测试不能验证宿主识别能力。
|
||||
|
||||
配置与壁纸构建通过,更新本机安装目录后需重新载入壁纸。真实系统音乐输入的恢复效果尚待宿主试用确认。律动目前作用于桌面工作台;需启用音乐律动并关闭减少动态效果。
|
||||
|
||||
参考:https://docs.wallpaperengine.io/en/web/audio/visualizer.html
|
||||
|
||||
## 呼吸开关与律动整理方案 · 2026-09-10
|
||||
|
||||
新增 WE「工作台 · 界面显示」中的「静止时呼吸」,默认开启。呼吸位移独立于选中档案归齐系数,因此「始终归齐」也可呼吸。关闭后平滑收回;开启后沿原来的 8 秒/13 秒缓慢周期恢复。音乐活动时淡出,安静后恢复,小游戏及减少动态效果下停止。
|
||||
|
||||
浏览器验证页增加独立开关与呼吸增益读数,方便组合检查。
|
||||
|
||||
当前音乐方案仍是低中高三组不同速度正弦波叠加,时间相位自行推进,音乐主要控制振幅,尚未进行节拍触发;持续人声和高频也会造成局部起伏。这是杂乱感的主要实现原因。
|
||||
|
||||
建议下一版采用「鼓点主导的宽波」:低频能量相对短时平均的上升沿触发,每次沿同一方向推进;设置约 180–240ms 防重复间隔、限制同时存在的波数,快速上升后缓慢回落。中频仅轻微调节整组振幅,高频不独立驱动位移。使用缓慢增益适配,限制峰值,安静时平滑退回呼吸。另一种更安静的方向是整组同步轻抬,几乎不做传播。此处记录方案,未替换现有音乐算法;合成输入检查不能代替真实歌曲试听。
|
||||
|
||||
## 三种律动并列试用 · 2026-09-10
|
||||
|
||||
用户比较后保留原版多层起伏为默认,并要求重做 A/B。当前 WE 属性提供原版、方案 A「左低中中右高频谱」、方案 B「连续层叠流波」,沿用原有存储值 legacy / wave / lift。切换连续混合,强度和独立呼吸开关共用。
|
||||
|
||||
A 按当前相机投影后的屏幕横向位置混合低/中/高频,避免斜视阵列的物理列号与屏幕左右不一致。相邻频段平滑衔接,持续音也会驱动起伏。B 使用低、中、高频驱动三层不同速度的连续流波,替代旧的整组节拍轻抬。原版保留原参数。两项新方案不再使用鼓点触发门槛;真实音乐观感待用户试用。
|
||||
|
||||
检查覆盖左右频段分布、持续中高频响应、频段边界、静音归零及快速切换。浏览器对照页 reference/workbench-revision.html 提供原版与 A/B、独立持续频段和混合输入。
|
||||
|
||||
## 阵列覆盖与性能
|
||||
|
||||
「入场与画面 → 阵列覆盖余量」提供「自动 · 标准余量」(默认)和「自动 · 额外余量」。两档都按当前镜头和屏幕范围计算数量,额外档多保留屏幕外余量,渲染开销较高。正常使用无需填写档案数量;切换屏幕比例和进入详情时自动重新计算。
|
||||
|
||||
屏幕外不再整池提交。仍保留可能进入视口或影响附近阴影的卡片;这不是对所有互相遮挡的表面做深度遮挡裁剪。相同比例、相同构图从 1080p 改到 4K,不会仅因像素增加而生成更多档案。更宽的视野会需要更多档案,不能把旧版缺失边角时的较低数量当作同等覆盖下的性能基准。
|
||||
|
||||
暗色远景灰雾与边缘淡出一起加强。验证与当前限制见 verification/ARRAY-COVERAGE.md。
|
||||
|
||||
## 工作台细节与开场衔接修订
|
||||
|
||||
移除「播放器未提供进度」和时区说明;有有效进度时继续显示时间线。暗色选中刻度使用暖白,REINITIALIZE 悬停使用琥珀色。接力退出按钮位于计时线下方,与设置入口分开。
|
||||
|
||||
工作台开场隐藏选档标题与编号;Powered 在开场末段 350ms 淡出。工作台与档案展示互切时,旧界面约 220ms 退场,输入立即隔离,完成后隐藏;快速返回衔接当前透明度。减少动态效果时直接显示或隐藏。
|
||||
|
||||
普通开场也按实际镜头视锥动态生成档案,覆盖超宽及竖屏;仅显式原片逐帧对照保留 160 个位置。详见 verification/WORKBENCH-REVISION.md。
|
||||
|
||||
本地媒体封面修复:文字、封面、进度按独立事件保留,不再因文字更新误清除封面。回归步骤与官方接口依据见 verification/WORKBENCH-REVISION.md。
|
||||
|
||||
游戏进出过渡:游戏信息 300ms 淡入并轻移,220ms 淡出;工作台与品牌、页脚反向渐变。入口与目标标记分别淡入淡出,退出完成后恢复操作与焦点。隐藏游戏入口和减少动态效果均继续生效。
|
||||
|
||||
## 可选 HUD 与画面质感
|
||||
|
||||
WE 属性中的「HUD 与画面质感」提供 UI 曲面视差、全局屏幕效果与界面磨砂底,默认全部关闭。
|
||||
|
||||
- 「曲面纵深」决定四周 UI 的展开程度。「鼠标追踪」独立控制跟随;关闭后仍保留静态曲面。三维背景不跟随被动鼠标移动。
|
||||
- 「全局屏幕效果」覆盖三维背景、文字、按钮、开场与弹窗。颗粒、颗粒尺寸、色散、暗角分别调节;可先单项从 0 调高来比较效果。
|
||||
- 磨砂底使用主题颜色并随文字和按钮投影。减少动态效果时停止鼠标跟随、冻结颗粒,静态形状仍保留。
|
||||
|
||||
实现、参考结构与验证见 verification/VISUAL-EFFECTS.md。
|
||||
|
||||
## 四边距与任务栏
|
||||
|
||||
WE「界面边距 · 正值内收 / 负值外扩」提供上、下、左、右四个独立滑杆,范围 −300~300px,默认 0。数值是在原布局上的额外偏移;正值留出更多空白,负值向屏幕边缘外扩。任务栏遮住底部时,可先把「下边距」调到 60,再按实际遮挡微调。四项设回 0 恢复原布局。
|
||||
|
||||
边距不依赖视差开关。工作台、档案 UI、固定游戏信息、弹窗与查看器控制区适配这些偏移,字体大小和三维画布不因边距缩放,开场保持原构图。详见 verification/UI-INSETS.md。
|
||||
|
||||
|
||||
2026-09-10 修订:曲面纵深、细颗粒、颗粒尺寸、边缘色散、暗角的默认数值统一为 20。下边距不再缩短桌面工作台的中部定位容器;底部导航和页脚先上移,实际接近中部内容时才推动对应内容,上方品牌限制继续上移,空间不足时内容独立滚动。手机窄屏继续采用原有纵向滚动布局。
|
||||
|
||||
WE 新增「工作台 · 可用功能」,时间日期、今日事项、重要日程、正在播放、专注计时各自可开关,默认全开。关闭功能同时移除其右侧内容入口和底部按钮;当前项关闭时选择首个仍开启功能,全关时隐藏右侧内容和导航。左侧时钟与事项仍由原显示开关控制,任务和计时状态保留。用户明确要求自行验证,本轮仅完成实现和构建打包,未进行运行时与视觉验收。
|
||||
|
||||
|
||||
2026-09-10 后续修正(覆盖上述中部避让方案):用户明确下边距只移动底部两排,排间距固定,中部组件不压缩、不滚动。已删除 workbench-insets 自动避让;工作台高度不再随下边距变化。底部导航、游戏入口与页脚在曲面投影之后使用相同 translate 偏移。另修复 :is 内 ID 提高优先级导致原规则覆盖,以及减少动态效果的隐式 all 过渡污染曲面测量的问题。Edge 实际页面完成 1920×1080、2560×1440、1600×900,曲面 0/20/80,下边距 60/192/300/−80/0,共 45 组检查:中部坐标及尺寸固定,无新增 overflow,底部各行位移一致。画面对照与数据见 verification/ui-insets/。
|
||||
|
||||
WE「入场与画面」新增暗色专用开关「暗色模式仅选中档案标签使用强调色」,默认关闭保留现有选择。开启后阵列与归位档案的顶边标签使用暗色,当前选中档案保留琥珀强调;浅色不受影响。通过既有材质 uniform 实现,无新增网格与绘制通道,暗色开/关及浅色截图已检查。
|
||||
|
||||
|
||||
## 超级性能模式与自定义画质
|
||||
|
||||
用户于 2026-09-10 要求低配壁纸模式与减少动态效果独立,并恢复 WE 原生自定义画质。新增「超级性能模式(保留动态效果)」开关,默认关闭;开启时以独立的有效画质覆盖已保存画质,不改减少动态效果、宿主帧率或音乐律动设置,关闭恢复当前预设/自定义配置。
|
||||
|
||||
超级模式:三维比例 60%、DPR 上限 1、总像素不超过 921600;关闭阵列阴影、AO、景深和 SMAA,直接输出主场景;背景阵列使用无折射、无清漆材质并隐藏小螺丝,选中/详情模型保留完整结构和低分辨率折射。全局屏幕滤镜暂时停用,文字磨砂改为无模糊底色,HUD 曲面与追踪继续运行。DOM 文字不降低分辨率。
|
||||
|
||||
WE 画质下拉增加「自定义」,仅此选择且超级模式关闭时显示九项控制:渲染比例、像素密度、抗锯齿、阴影、AO 采样、AO 分辨率、景深、透明材质分辨率、纹理过滤。连续参数用滑杆,离散参数用原生下拉。部分宿主回调保留其他参数;超级模式开启期间保留自定义更新,关闭恢复最新配置。完整证据见 verification/SUPER-PERFORMANCE.md。
|
||||
|
||||
## 暂停三维渲染与自定义图片
|
||||
|
||||
点击左下 SESSION 右侧「3D 开启」:档案依次下落,完成后释放主场景及查看器资源,保留 2D 工作台。点击「3D 关闭」重新加载,档案从下方升起;退场途中再次点击会恢复。游戏入口在退场、关闭、载入期间隐藏,成功开启后按原偏好恢复。
|
||||
|
||||
在 WE「入场与画面」启用「关闭 3D 后显示自定义壁纸」,再选择图片,可在释放完成后淡入该图;重新加载成功后图片淡出。默认不开启,未选图则继续使用原背景。图片不会成为默认资产或写入项目。验证与限制见 verification/THREE-RELEASE.md。
|
||||
|
||||
本轮保留原版、连续频谱和层叠流波三种音乐律动;同时修复无音乐拖动后的错误归齐、全归齐时标签强调与暗色选中强调过渡,见 verification/SELECTION-STATE.md。
|
||||
|
||||
## 创意工坊发布准备
|
||||
|
||||
- 用户于 2026-09-10 曾授权发布,随后明确改为自行操作发布,代理负责 GIF 和操作说明;当前尚未上传。
|
||||
- 正式名称为 Rhine Lab · 莱茵生命交互桌面,去掉“本地测试”。独立正式工程放在 Wallpaper Engine 的 myprojects/rhine-lab-workshop,保留之前本地试用工程。
|
||||
- 预览参照用户指定的 3088099655 预览的全景/局部剪辑方法,素材全部来自本工程工作台实际运行画面。最终 GIF 256×256、13.2 秒,含时钟事项、专注与明暗切换;没有将参考壁纸的图片放入发行包。
|
||||
- 发布操作和后续更新须保留 workshopid,见 docs/WORKSHOP-PUBLISH.md;预览来源与验证见 verification/WORKSHOP-PREVIEW.md。
|
||||
|
||||
## 仅加载 2D 启动与图片路径修复
|
||||
|
||||
WE「入场与画面」新增「启动时加载 3D(下次加载生效)」,默认开启。关闭后不创建 WebGL 场景或下载模型,2D 开场到原片 26.9 秒处直接进入组件页面;左下 3D 开关可手动加载。改变属性不会卸载当前场景。自定义图片在开场结束后出现。
|
||||
|
||||
自定义图片路径修复了宿主将盘符编码为 E%3A 后再次编码导致的读取失败;支持原始路径、编码路径与 file URL。同一路径读取失败后重新选择可重试。真实 WE 宿主验证提取的 wallpaper-2.jpg 为 3200×2000 并成功解码。
|
||||
|
||||
验证:scripts/check-startup-2d.mjs 检查初始无 canvas/GLB 请求、2D 开场结束、手动载入、属性仅下次启动生效、默认开启与无页面异常;scripts/check-wallpaper-image.mjs 验证路径与同路径重试;scripts/check-wallpaper-image-host.mjs 验证真实宿主回调。结果位于 verification/startup-2d 和 verification/wallpaper-image。
|
||||
|
||||
### 宿主属性到达时序修复
|
||||
|
||||
启动必须等待 WE 首次 applyUserProperties 回调后再判断是否创建三维场景,不能将尚未收到的属性当作默认开启。真实 file 壁纸没有超时后强行加载的回退;普通 HTTP 预览无宿主时继续正常启动。此前测试只覆盖提前注入属性,现补充 1800ms 延迟回调;真实 WE 独立窗口验证关闭属性后进入 archive,canvas 与 GLB 请求均为 0(scripts/check-startup-2d-host.mjs,verification/startup-2d/host-results.json)。
|
||||
|
||||
## 自定义图片遮罩范围
|
||||
|
||||
WE 自定义壁纸图片下方新增「上下遮罩范围(0 为关闭)」:0–100,默认 100 保留原渐变范围;降低数值缩小上下覆盖,0 完全隐藏图片上的 atmosphere。仅图片实际显示时生效。亮暗配色 100/50/0 的样式断言及截图见 scripts/check-wallpaper-mask.mjs 与 verification/wallpaper-mask;构建通过。
|
||||
|
||||
## 时钟与媒体滚动、图片背景按钮可读性
|
||||
|
||||
工作台时钟、页脚秒钟、媒体已播放/总时长、切歌标题与艺人、专注计时复用现有 createRollingText,460ms、direct、同时起动与轻微模糊;格式化时间按字符滚动,冒号保持。首次显示直接初始化,相同值不重复播放,减少动态效果立即完成。媒体面板原位更新,保留滚动节点及未变化封面,避免宿主高频进度回调打断。
|
||||
|
||||
自定义图片显示时,页脚、底部导航、待办事项与提示、媒体及其他按钮改用主题前景色,关闭 3D 状态不降低文字透明度。用户否决额外白底,已撤回该底色与外扩阴影,仅保留原有玻璃模糊。无遮罩图片背景实看与时钟、切歌、进度、专注、减少动态效果检查见 scripts/check-workbench-rolling.mjs 和 verification/workbench-rolling。
|
||||
|
||||
|
||||
- 页脚 HH:mm:ss 进一步改为三组 createRollingNumber,冒号固定、两位补零、460ms 向上滚动。验证捕获实际运行中的动画,并检查 12:59:59→13:00:00 进位;减少动态效果时直接到位。此前此处采用整段 createRollingText,现已替换。
|
||||
|
||||
### 曲面下 8、9 与歌曲文字滚动中断
|
||||
|
||||
复现:启用 HUD 曲面后,秒数 7→8、8→9 的动画开始约 120ms 时已被清空,其他数字仍在运动。Rolling Number 0.4.1 用透视后的边界反推字形尺寸,与 ResizeObserver 的本地尺寸产生误差,触发 refresh 并直接结束动画。
|
||||
|
||||
scripts/patch-rolling-number.mjs 将字形宽高改为本地 computed style 尺寸,保留原滚动算法;安装、开发及构建自动应用,依赖升级后匹配失败会明确报错。验证开启曲面后的全部 0–9、9→10、小时进位以及五组不同字宽/长度歌曲名,120ms 时均有运行中的动画。证据见 verification/workbench-rolling/results.json。
|
||||
|
||||
- 播放器长标题与艺人名单行显示,按可用宽度截断为省略号后交给滚动库;悬停与辅助标签保留完整文字,视口变化重新计算。验证包含超长标题省略号与完整提示。
|
||||
|
||||
### 开场 HUD
|
||||
|
||||
开启「HUD 与画面质感」中的曲面效果后,前段开场也应用相同曲面纵深与鼠标追踪。关闭追踪保留静态曲面,关闭 HUD 恢复原开场;减少动态效果时停止追踪。无需新增开关。原始开场位移、缩放与笔画时间轴继续运行,进入工作台时延续 HUD 状态。
|
||||
@@ -0,0 +1,46 @@
|
||||
【更新日志|最新在前】
|
||||
|
||||
2026.09.11
|
||||
• 修正开场 Logo 下方 RHINE·LAB 小字的重复重绘问题,改善开启 HUD 时的文字稳定性。
|
||||
• HUD 曲面与鼠标追踪扩展到开场:Logo、验证文字、圆环与欢迎画面沿用同一效果,并与工作台连续衔接。
|
||||
• 修复部分数字(如 8、9)和切歌文字的滚动动画中断;长歌名、歌手名自动省略,悬停可查看完整内容。
|
||||
• 页脚时钟改为时、分、秒独立滚动,支持连续进位。
|
||||
• 工作台时钟、专注倒计时、播放器时间及切歌文字加入滚动过渡;减少动态效果时直接显示。
|
||||
• 改善自定义图片背景下底部按钮、导航、待办和提示文字的对比度,保留原有玻璃背景。
|
||||
|
||||
2026.09.10
|
||||
• 自定义图片新增上下遮罩范围调节,设为 0 可完全关闭遮罩。
|
||||
• 新增「启动时加载 3D」开关:关闭后只播放前段 2D 入场,之后可手动加载模型;修复首次读取设置时仍加载 3D 的问题。
|
||||
• 修复本地图片路径识别与载入重试;支持关闭 3D 后显示自定义壁纸。
|
||||
• 左下角新增 3D 开关:档案依次退场后释放三维资源,再次开启重新载入并升起。
|
||||
• 新增超级性能模式,保留动态效果;补全自定义画质控制,关闭性能模式后恢复之前的画质。
|
||||
• 新增四边独立界面边距,支持向内或向外调整;修正下边距,让底部两排一起移动,中部组件保持位置。
|
||||
• 暗色模式新增「仅选中档案标签使用强调色」选项,并修正归齐状态下的标签表现。
|
||||
• 新增 HUD 曲面、独立鼠标追踪、局部界面磨砂,以及覆盖整个画面的颗粒、色散与暗角;各项可独立调整。
|
||||
• 阵列根据镜头和屏幕范围自动补位,裁剪屏幕外档案,改善宽屏边角露出阵列尽头的问题;加强暗色边缘雾气。
|
||||
• 新增亮色/暗色配色及逐张变色过渡,覆盖工作台、档案详情与独立查看器。
|
||||
• 提供原版、连续频谱、层叠流波三种音乐律动;静止呼吸可独立开关。修复壁纸自身音效触发音乐归齐的问题。
|
||||
• 新增波纹接力小游戏,完善游戏与工作台的进出过渡。
|
||||
• 新增桌面工作台:时间日期、今日事项、重要日程倒计时、系统媒体信息和专注计时;支持按需隐藏内容与关闭功能入口。
|
||||
• 修复切歌时封面、歌曲文字和进度相互覆盖的问题,完善工作台入场、退场与操作衔接。
|
||||
• 开场适配不同屏幕比例,移除底部字幕,并提供完整/简短入场选择。
|
||||
• 完成 Wallpaper Engine 适配:本地资源打包、原生属性分组、音频响应、媒体信息、宿主帧率与暂停通知。
|
||||
|
||||
────────────────────
|
||||
|
||||
【Rhine Lab · 莱茵生命交互桌面】
|
||||
以莱茵生命档案终端为灵感的非官方同人交互壁纸。
|
||||
|
||||
【桌面工作台】
|
||||
时间与日期、今日事项、重要日程倒计时、系统媒体信息、专注计时,可按需显示或隐藏。
|
||||
|
||||
【三维档案】
|
||||
可拖动的循环玻璃档案阵列、惯性滑动、档案详情与 360° 模型拆解查看。
|
||||
|
||||
【画面与交互】
|
||||
亮色/暗色配色、三种音乐律动、呼吸效果、HUD 曲面与鼠标追踪、波纹接力小游戏。支持自定义画质与超级性能模式;可关闭 3D 并显示自己的图片背景。
|
||||
|
||||
【使用】
|
||||
在 Wallpaper Engine 属性中切换模式、填写事项与日程、调整画面。拖动阵列浏览,点击档案读取详情;音乐律动需要启用音频响应。媒体信息由系统与播放器提供。
|
||||
|
||||
预览主要展示工作台模式,事项为演示内容;实际事项由你填写。资源本地运行。角色、名称与相关世界观归原权利方所有,本作品与官方无关联。
|
||||
@@ -0,0 +1,54 @@
|
||||
# 手动发布到 Wallpaper Engine 创意工坊
|
||||
|
||||
正式名称:**Rhine Lab · 莱茵生命交互桌面**。
|
||||
|
||||
正式本地工程已关联创意工坊条目 `3799142774`,后续使用「发布更新」。
|
||||
|
||||
工程目录:
|
||||
|
||||
```text
|
||||
D:\Game\Steam\steamapps\common\wallpaper_engine\projects\myprojects\rhine-lab-workshop
|
||||
```
|
||||
|
||||
## 第一次发布
|
||||
|
||||
1. 确保 Steam 在线,打开 Wallpaper Engine。如果已经打开过,重新打开壁纸选择界面,让它刷新本地工程。
|
||||
2. 在「已安装」里搜索 `Rhine Lab`,选择 **Rhine Lab · 莱茵生命交互桌面**。另一个带「本地测试」的条目是之前的试用工程,本次使用新名称的条目。
|
||||
3. 点击右侧详情里的 **在编辑器中打开**。
|
||||
4. 在编辑器顶部打开 **创意工坊** 菜单,选择分享/发布壁纸的选项,进入 **发布到创意工坊** 表单。不同版本的菜单可能称为「准备发布」或「在创意工坊上分享壁纸」。这是发布完整壁纸,不是发布组件或属性预设。[官方网页壁纸发布流程](https://docs.wallpaperengine.io/en/web/first/gettingstarted.html)
|
||||
5. 核对标题为 **Rhine Lab · 莱茵生命交互桌面**。描述已写入工程;如果界面未带出,可从本文末尾复制。
|
||||
6. 设置 **可见性:公开**、**年龄分级:大众级(Everyone)**;样式可选 **科技(Technology)**。
|
||||
7. 在 **预览图片 → 导入文件** 中选择下面的 GIF。即使已经出现封面,也可以重新导入这一版。
|
||||
|
||||
```text
|
||||
D:\Game\Steam\steamapps\common\wallpaper_engine\projects\myprojects\rhine-lab-workshop\preview.gif
|
||||
```
|
||||
|
||||
8. 第一次上传选择 **创建新项目**,点击 **发布**,等待提示上传成功。
|
||||
9. 点击 **在创意工坊中显示壁纸**,检查正式名称、动画封面和可见性。如果 Steam 要求接受创意工坊协议,由你在 Steam 页面完成。若页面提示审核中,等待审核即可,不要重复创建条目。
|
||||
10. 保存工坊页面链接,并备份整个 `rhine-lab-workshop` 工程目录,尤其是发布后更新的 `project.json`。
|
||||
|
||||
## GIF 说明
|
||||
|
||||
工坊封面为 256×256、13.2 秒、约 940 KB,包含工作台全景、时钟与事项特写、专注计时特写、暗色全景与暗色特写,再回到亮色全景。它从真实浏览器画面剪辑并裁切放大,使用演示事项及演示频谱;演示事项没有写入壁纸的默认值。
|
||||
|
||||
GIF 是无声的。预览封面不能完整表现所有细节,订阅后的壁纸按实际屏幕尺寸渲染。官方规定 GIF 预览不超过 256×256、文件不超过 1 MB;当前文件符合该限制。[官方预览说明](https://docs.wallpaperengine.io/en/scene/first/publishing.html#animated-preview-images)
|
||||
|
||||
## 以后如何更新
|
||||
|
||||
可以持续更新同一个工坊条目,原订阅者会收到更新,无需重新订阅。[官方更新说明](https://docs.wallpaperengine.io/en/scene/first/publishing.html#publishing-updates)
|
||||
|
||||
1. 更新这个原工程的壁纸资源,保留 `project.json` 中的 **workshopid**,以及原发布关联。
|
||||
2. 从同一条目选择 **在编辑器中打开**,再次进入发布窗口。
|
||||
3. 「上传为」应为 **更新现有项目**,按钮应显示 **发布更新**;填写本次更新说明并发布。
|
||||
4. 如果变成「创建新项目」,先检查是否打开了其他工程或丢失工坊 ID,不要直接发布一个重复条目。
|
||||
|
||||
源码仓库的 `npm run build:wallpaper` 只生成构建包,不会自动读取 Steam 刚生成的工坊 ID。首次发布后,不要用尚未包含该 ID 的构建版 `project.json` 直接覆盖已发布工程的同名文件。后续交给代理更新时,提供这个工程目录或工坊链接,让代理先保留发布关联再同步资源。
|
||||
|
||||
工坊上传不等于源码备份。保留源码仓库和发布工程,两者都有用途。
|
||||
|
||||
## 可复制的发布描述
|
||||
|
||||
完整简介保存在 [WORKSHOP-DESCRIPTION.txt](WORKSHOP-DESCRIPTION.txt),并同步到正式工程的 description。更新日志放在最前面,按日期从新到旧,同一天按更新顺序从新到旧排列,后接功能介绍。日期依据开发记录整理,不代表当天每一项都单独发布过工坊版本。以后完成更新时,在简介顶部追加面向用户的变化。
|
||||
|
||||
若发布窗口未自动带出新简介,将该文本文件的完整内容粘贴到描述栏,再发布更新。
|
||||
@@ -0,0 +1,61 @@
|
||||
# README 演示素材
|
||||
|
||||
2026-09-09 从当前应用重新采集,运行版本为 `e0cb75ffcc1bf7d1fb5e2458b3c35fa0f6f336da`。使用独立的 Chrome 浏览器上下文、GPU 硬件加速与默认「原始」画质,不读取日常浏览器的收藏和偏好。素材仅用于文档展示,不参与应用运行。
|
||||
|
||||
## 静态截图
|
||||
|
||||
全部截图为 1600 × 900 JPEG,质量参数 93。保留真实界面与模型,没有叠加宣传文字或替换背景。
|
||||
|
||||
| 文件 | 展示内容 |
|
||||
| --- | --- |
|
||||
| `archive.jpg` | 新版循环阵列与档案导航 |
|
||||
| `detail.jpg` | 解密完成后的双环内构与档案概述 |
|
||||
| `research.jpg` | 研究记录页签 |
|
||||
| `viewer-clear.jpg` | 独立查看器,清晰玻璃 |
|
||||
| `viewer-frosted.jpg` | 相同视角下的磨砂玻璃 |
|
||||
| `assembly.jpg` | 六组结构拆解 |
|
||||
| `search.jpg` | 搜索「莱茵」后的档案索引 |
|
||||
| `settings.jpg` | 音效、音乐、减少动态效果与画质设置 |
|
||||
| `boot.jpg` | 白底开场中的 Logo 与身份接入 |
|
||||
|
||||
## 动图
|
||||
|
||||
通过 Chrome DevTools Protocol 录制运行页面,按捕获时间戳保留真实节奏,再用 FFmpeg 缩放、降帧和量化。GIF 使用 96 色调色板与 Bayer 抖动,无声音,不代表应用实时帧率。浏览器仅在合成帧变化时输出录制帧,静止画面依照原时间保持。
|
||||
|
||||
| 文件 | 内容 | 大约时长 | 尺寸 / 帧率 |
|
||||
| --- | --- | --- | --- |
|
||||
| `decryption.gif` | 抽取、对角解密线、玻璃与正文同步揭示 | 7.9 秒 | 960 × 540 / 12 fps |
|
||||
| `glass-motion.gif` | 清晰 → 磨砂 → 清晰 | 4.2 秒 | 800 × 450 / 12 fps |
|
||||
| `assembly-motion.gif` | 拆解、旋转、视角复位与重组 | 8.0 秒 | 800 × 450 / 12 fps |
|
||||
| `browse.gif` | 连续选档、切列和文字滚动 | 6.8 秒 | 640 × 360 / 8 fps |
|
||||
| `boot-motion.gif` | 白底输入、Logo、权限扫描与欢迎转场 | 20.4 秒 | 800 × 450 / 12 fps |
|
||||
|
||||
README 直接展示解密动图,其余动图放在可展开区域。采集时间、源码版本与原始帧间隔记录见 [`capture.json`](capture.json);GIF 编码的帧时长取整可能使成片时长略有差异。
|
||||
|
||||
## 重新采集
|
||||
|
||||
采集脚本为 [`scripts/capture-readme.mjs`](../../scripts/capture-readme.mjs)。需要 Chrome、Playwright 与 FFmpeg;它们是文档制作工具,普通应用运行不需要安装。脚本会覆盖本目录同名素材,并将中间 JPEG 帧保存在被 Git 忽略的 `.tools/readme-capture/`。
|
||||
|
||||
先启动应用:
|
||||
|
||||
```sh
|
||||
npm ci
|
||||
npm run dev -- --port 5186
|
||||
```
|
||||
|
||||
再从仓库根目录执行脚本。已能通过 Node 导入 Playwright、且 FFmpeg 在 PATH 中时:
|
||||
|
||||
```sh
|
||||
node scripts/capture-readme.mjs
|
||||
```
|
||||
|
||||
也可以指定现有工具的位置,例如 PowerShell:
|
||||
|
||||
```powershell
|
||||
$env:PLAYWRIGHT_MODULE = 'C:/tools/node_modules/playwright/index.mjs'
|
||||
$env:FFMPEG = 'C:/tools/ffmpeg.exe'
|
||||
$env:CAPTURE_URL = 'http://127.0.0.1:5186'
|
||||
node scripts/capture-readme.mjs
|
||||
```
|
||||
|
||||
其中工具路径需要替换为本机的实际位置。Windows 录制显式使用 D3D11;其他平台使用 Chrome 可用的 GPU 后端。录制后应查看截图、GIF 的起止帧及中间过渡,确认资源加载完整、没有遮挡或异常,检查 README 图片链接,并更新此处的版本与时长。
|
||||
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 6.0 MiB |
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"commit": "e0cb75ffcc1bf7d1fb5e2458b3c35fa0f6f336da",
|
||||
"capturedAt": "2026-09-09T04:21:30.572Z",
|
||||
"viewport": "1600x900",
|
||||
"quality": "original",
|
||||
"gifs": [
|
||||
{
|
||||
"name": "browse",
|
||||
"frames": 126,
|
||||
"duration": 6.623064994812012,
|
||||
"width": 640,
|
||||
"fps": 8,
|
||||
"averageCaptureFps": 19.02442450718791,
|
||||
"maximumFrameGap": 0.3434779644012451
|
||||
},
|
||||
{
|
||||
"name": "decryption",
|
||||
"frames": 200,
|
||||
"duration": 7.776905059814453,
|
||||
"width": 960,
|
||||
"fps": 12,
|
||||
"averageCaptureFps": 25.71717135052331,
|
||||
"maximumFrameGap": 0.14150595664978027
|
||||
},
|
||||
{
|
||||
"name": "glass-motion",
|
||||
"frames": 114,
|
||||
"duration": 4.078908920288086,
|
||||
"width": 800,
|
||||
"fps": 12,
|
||||
"averageCaptureFps": 27.948650540583383,
|
||||
"maximumFrameGap": 0.06445097923278809
|
||||
},
|
||||
{
|
||||
"name": "assembly-motion",
|
||||
"frames": 211,
|
||||
"duration": 7.880533933639526,
|
||||
"width": 800,
|
||||
"fps": 12,
|
||||
"averageCaptureFps": 26.77483553484964,
|
||||
"maximumFrameGap": 0.09002685546875
|
||||
},
|
||||
{
|
||||
"name": "boot-motion",
|
||||
"frames": 158,
|
||||
"duration": 20.333863019943237,
|
||||
"width": 800,
|
||||
"fps": 12,
|
||||
"averageCaptureFps": 7.7702893859880575,
|
||||
"maximumFrameGap": 1.0142180919647217
|
||||
}
|
||||
],
|
||||
"screenshots": [
|
||||
"archive",
|
||||
"detail",
|
||||
"research",
|
||||
"viewer-clear",
|
||||
"viewer-frosted",
|
||||
"assembly",
|
||||
"search",
|
||||
"settings",
|
||||
"boot"
|
||||
],
|
||||
"errors": []
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.4 MiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 305 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 74 KiB |
@@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#e8e5e1" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="apple-mobile-web-app-title" content="Rhine Lab" />
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Rhine Lab — Synthesize Information Analysis OS. 交互式三维研究档案终端。"
|
||||
/>
|
||||
<title>RHINE LAB · ANALYSIS OS</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
</head>
|
||||
<body>
|
||||
<main id="viewport"><div id="stage"></div></main>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
32144
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "rhine-lab-analysis-os",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"postinstall": "node scripts/patch-rolling-number.mjs",
|
||||
"predev": "node scripts/patch-rolling-number.mjs && node scripts/archive-source.mjs",
|
||||
"dev": "vite --host 127.0.0.1",
|
||||
"dev:archive": "node scripts/archive-dev.mjs",
|
||||
"prebuild": "node scripts/patch-rolling-number.mjs && node scripts/archive-source.mjs && node scripts/prepare-webfonts.mjs",
|
||||
"check:content": "node --experimental-strip-types --test scripts/check-content.mjs",
|
||||
"check:viewport": "node --experimental-strip-types scripts/check-viewport.mjs",
|
||||
"review:mobile": "node scripts/serve-mobile-review.mjs",
|
||||
"export:archives": "node scripts/export-records.mjs",
|
||||
"archive:snapshot": "node scripts/archive-source.mjs",
|
||||
"archive:serve": "node scripts/archive-service.mjs",
|
||||
"build": "tsc && vite build && node scripts/build-pwa.mjs",
|
||||
"build:wallpaper": "node scripts/patch-rolling-number.mjs && node scripts/export-records.mjs && tsc && vite build --mode wallpaper --outDir release/wallpaper && node scripts/build-wallpaper.mjs",
|
||||
"format": "prettier --write src scripts index.html package.json tsconfig.json",
|
||||
"preview": "vite preview --host 127.0.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kitlangton/rolling-number": "0.4.1",
|
||||
"three": "^0.183.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/three": "^0.183.0",
|
||||
"prettier": "^3.9.6",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
Opening lettering / 开场文字
|
||||
|
||||
Novecento Sans Wide, designed by Jan Tonellato / Synthview Type Design.
|
||||
https://typography.synthview.com/novecento-sans-font-family.php
|
||||
|
||||
Normal, DemiBold and Bold were used to draw the fixed English phrases in the
|
||||
opening and the fixed RHINE LAB corner heading.
|
||||
The source repository contains pre-authored phrase graphics. A separately
|
||||
licensed local build may include the official MyFonts webfont kit and render
|
||||
these phrases using its original WOFF2 files. The kit is excluded from Git.
|
||||
Original font copyrights and ownership remain with Jan Tonellato / Synthview.
|
||||
The project's MIT license does not relicense the underlying typeface.
|
||||
|
||||
Normal and DemiBold source: author's DaFont package, free desktop license.
|
||||
https://www.dafont.com/novecento-sans.font
|
||||
https://typography.synthview.com/pdf/eula-free-font-dafont.pdf
|
||||
Bold source: user's existing MyFonts desktop package.
|
||||
|
||||
Both source licenses permit characters to be imported as graphical objects
|
||||
(letters outlined as graphics). The desktop font files must not be redistributed
|
||||
or embedded in this project. Changing the authored phrases requires separately
|
||||
obtaining the font and rerunning scripts/make-boot-lettering.py locally.
|
||||
|
||||
Optional official webfont kit:
|
||||
MyFonts / Monotype Font Software For Web Content EULA (v260616).
|
||||
https://www.myfonts.com/pages/license-agreement?id=eula_2267&eula_lang=eula_en
|
||||
The fonts are individually licensed to the website owner. Other users must
|
||||
obtain their own license; this project's MIT license does not cover them.
|
||||
Retain the original kit's RhineLabNovecento.css copyright and license notice.
|
||||
Webfont rights are limited by the owner's license term, domain and pageviews.
|
||||
@@ -0,0 +1,12 @@
|
||||
# Observatory / 观测室
|
||||
|
||||
三个 Ogg 文件为本项目原创程序编配的同步循环声部,MP3 是带首尾淡变的独立试听版。配乐无原片采样、无外部录音、无人声。
|
||||
|
||||
源谱及合成:`scripts/render-audio.mjs`;音效合成:`src/audio.ts`。
|
||||
生成数据:`score.json`。随项目采用仓库 LICENSE。
|
||||
|
||||
## 逐字输入短音
|
||||
|
||||
`typing-preview.wav` 及 `src/typing-samples.ts` 使用用户提供的《明日方舟》特别映像 [莱茵生命:访问] 中约 6.864–6.986 秒的三个短音,各 38ms。`typing-source.json` 保留精确时间、源文件校验与处理参数。原音及其衍生片段的权利归原作者,不纳入上文原创配乐的 MIT 授权声明。
|
||||
|
||||
提取脚本:`scripts/extract-typing-audio.mjs`。原片短音仅去直流、做边缘淡变和统一增益,没有变调、变速或合成替换。`reference/typing-original.wav` 为本地对照片段,不属于生产配乐。
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"title": "Observatory / 观测室",
|
||||
"bpm": 72,
|
||||
"duration": 53.333333333333336,
|
||||
"rate": 48000,
|
||||
"seed": 93271,
|
||||
"metrics": {
|
||||
"atmosphere": {
|
||||
"peakDb": -10.334112082610385,
|
||||
"rmsDb": -24.625500342815418,
|
||||
"seamDelta": 0.0005690408870577812
|
||||
},
|
||||
"motif": {
|
||||
"peakDb": -12.383920130269303,
|
||||
"rmsDb": -25.642892727244533,
|
||||
"seamDelta": 0.0004476997419260442
|
||||
},
|
||||
"pulse": {
|
||||
"peakDb": -19.061942349249723,
|
||||
"rmsDb": -35.03524633636361,
|
||||
"seamDelta": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"source": "《明日方舟》特别映像 [莱茵生命:访问] [BV1rr4y1b7sz].mp4",
|
||||
"sourceSha256": "a009a9501ecac5ff93103933fc4009c05e82225e6da8f85dee1a77013286c5a9",
|
||||
"sampleRate": 48000,
|
||||
"channels": 1,
|
||||
"cuts": [
|
||||
{
|
||||
"start": 6.864,
|
||||
"end": 6.902
|
||||
},
|
||||
{
|
||||
"start": 6.906,
|
||||
"end": 6.944
|
||||
},
|
||||
{
|
||||
"start": 6.948,
|
||||
"end": 6.986
|
||||
}
|
||||
],
|
||||
"processing": {
|
||||
"dcRemoval": true,
|
||||
"fadeInMs": 0.35,
|
||||
"fadeOutMs": 3.5,
|
||||
"sharedGain": 0.6230600941776551,
|
||||
"denoising": false,
|
||||
"pitchChange": false
|
||||
},
|
||||
"scope": "Three short excerpts from user-provided reference. Not original synthesis; source rights remain with the original creators."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect width="100" height="100" fill="#e8e5e1"/><path d="M50 50C35 25 9 29 9 50s26 25 41 0 41-25 41 0-26 25-41 0" fill="none" stroke="#111" stroke-width="10"/><path d="M21 50h17m-8-8v16m32-8h17" stroke="#111" stroke-width="5"/></svg>
|
||||
|
After Width: | Height: | Size: 295 B |
@@ -0,0 +1,14 @@
|
||||
MiSans by Xiaomi / 小米科技有限责任公司
|
||||
Copyright © 2020-2025 Beijing Xiaomi Mobile Software Co.,Ltd. All Rights Reserved.
|
||||
|
||||
Official source: https://hyperos.mi.com/font/download
|
||||
Archive: https://hyperos.mi.com/font-download/MiSans.zip
|
||||
The application now uses third-party split webfonts from [email protected]
|
||||
(font Version 4.003), distributed with this application for on-demand loading.
|
||||
Font shards are copied byte-for-byte from that package. CSS family names,
|
||||
weight mappings and URLs are adapted for the existing interface.
|
||||
Upstream copyright: Copyright © 2020-2023 Beijing Xiaomi Mobile Software Co.,Ltd. All Rights Reserved.
|
||||
Upstream project: https://github.com/mobeicanyue/misans-webfont
|
||||
Package source, integrity and file hashes: misans-webfont-4.3.1/source.json
|
||||
Upstream README: misans-webfont-4.3.1/UPSTREAM-README.md
|
||||
Font license: MiSans-license.pdf (retained separately from upstream tooling license).
|
||||
@@ -0,0 +1,83 @@
|
||||
# Misans Webfont <br> Misans 网页字体
|
||||
|
||||
本仓库旨在对 Misans 字体进行分包,方便字体在网页中加载。仅供学习交流使用。
|
||||
|
||||
[](https://www.npmjs.com/package/misans-webfont)
|
||||
[](https://www.jsdelivr.com/package/npm/misans-webfont)
|
||||
|
||||
本仓库使用 Github Action,利用[中文网字计划](https://chinese-font.netlify.app/)开发的[字体分包工具](https://github.com/KonghaYao/cn-font-split)对原字体分包,网页加载时只需获取所使用的文字所在的分包,大幅降低所需加载的大小,提升网页加载速度。
|
||||
|
||||
本仓库使用 Apache License 2.0 开源许可证,欢迎 PR, Star.
|
||||
|
||||
## 使用
|
||||
直接下面提供的代码以 `<link>` 的形式添加到网页的 `<head>` 内即可:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/misans-webfont/misans-style.css" />
|
||||
<style>
|
||||
body {
|
||||
font-family: "MiSans Bold";
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
```
|
||||
|
||||
|
||||
你也可以只引用单一的字体:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/misans-webfont/misans/misans-bold/result.min.css" />
|
||||
<style>
|
||||
body {
|
||||
font-family: "MiSans Bold";
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
```
|
||||
|
||||
具体可以引用的字体请查看:[fonts 分支](https://github.com/mobeicanyue/misans-webfont/tree/fonts),找到具体字体中的 `result.css`.
|
||||
|
||||
---
|
||||
|
||||
以下信息摘录自小米官网,使用字体请遵守下文的许可协议。
|
||||
|
||||
## MiSans 简介
|
||||
|
||||

|
||||
|
||||
MiSans Global 是由小米主导,联合蒙纳字库、汉仪字库共同打造的全球语言字体定制项目。这是一个庞大的字体家族,涵盖 20 多种书写系统,支持 600 多种语言,字符数量超过 10 万个。作为 Xiaomi HyperOS 系统默认字体,我们以简约/清晰,人文/易读,统一的视觉风格为基本原则出发,构建多语言信息体验一致性,旨在帮助为 Xiaomi HyperOS 提供互联的通用体验。
|
||||
|
||||
## Misans 许可协议
|
||||
|
||||
> 本《MiSans 字体知识产权许可协议》(以下简称“协议”)是您与小米科技有限责任公司(以下简称“小米”或“许可方”)之间有关安装、使用 MiSans 字体(以下简称“MiSans”或“MiSans 字体”)的法律协议。您在使用 MiSans 的所有或任何部分前,应接受本协议中规定的所有条款和条件。安装、使用 MiSans 的行为表示您同意接受本协议所有条款的约束。否则,请不要安装或使用 MiSans,并应立即销毁和删除所有 MiSans 字体包。
|
||||
>
|
||||
> 根据本协议的条款和条件,许可方在此授予您一份不可转让的、非独占的、免版税的、可撤销的、全球性的版权许可,使您依照本协议约定使用 MiSans 字体,前提是符合下列条件:
|
||||
> 1. 您应在软件中特别注明使用了 MiSans 字体。
|
||||
> 2. 您不得对 MiSans 字体或其任何单独组件进行改编或二次开发。
|
||||
> 3. 您不得单独将 MiSans 字体或其组件对外租赁、再许可、给予、出借或进一步分发字体软件或其任何副本以及重新分发或售卖。此限制不适用于您使用 MiSans 字体创作的任何其他作品。如您使用 MiSans 字体创作宣传素材、logo、应用 App 等,您有权分发或出售该作品。
|
||||
|
||||
## Misans FAQ
|
||||
|
||||
#### MiSans Global 需要付费吗?
|
||||
不需要。MiSans Global 所有的字体都是供全球免费商用,您可以在任何平台、任何商业项目中使用所有字体。
|
||||
|
||||
#### 是否可以用作嵌入式字体?
|
||||
可以。但您应在软件中特别注明使用了 MiSans 字体。
|
||||
|
||||
#### 我可以在软件中修改编辑、二次创作字体吗?
|
||||
您可以自由调节字体的粗细、间距等。但您不得单独将 MiSans 字体或其组件进行外观上的更改。
|
||||
|
||||
#### 什么是可变字体?
|
||||
可变字体是 OpenType 在 1.8 版规范中引入的扩展规范,简单说明就是一个字体文件同时支持多个字体形态,可以将多个字体文件合在单个字体文件中,通过定义字体内的变化来实现单轴或多轴设计空间。
|
||||
|
||||
#### 是否所有字体都支持可变?
|
||||
MiSans L3 生僻字字库(GB18030-2022 级别 3)仅支持 Regular 字重。其他所有字体均支持可变。
|
||||
|
||||
#### MiSans Global 的制作分工
|
||||
由小米主导,联合蒙纳、汉仪共同制作完成。
|
||||
其中蒙纳负责泰语、老挝及印度的所有地区语言。
|
||||
汉仪负责繁体中文、阿拉伯语、藏文、缅甸、高棉。
|
||||