# CBZ 章节化连续阅读(锁章 + 预读)设计 / CBZ chapter-scoped continuous reading design 日期 2026-09-08。分支 `feat/ui-redesign`。状态:已获用户批准(会话内确认)。 ## 目标 / Goal 漫画(CBZ)阅读以「章」为单位:一章内无论多少图都视为一段连续的整章内容, 默认不能靠上下滑动越过章界进入下一章(只能点「下一章」),可开「连读」让滑动贯穿章节。 去掉现有 连读/整页/适高 三档概念。锁章时预渲染(挂载+解码)其后 N 章(用户可配置,默认 2), 切章零白屏。 CBZ reading is chapter-scoped: images within a chapter flow continuously; by default scrolling cannot cross a chapter boundary (only the 下一章 button can); an optional 连读 mode lets scrolling flow into following chapters. The three-mode cycle (连读/整页/适高) is removed. The next N chapters (user-configurable, default 2) are pre-rendered while locked so chapter switches are instant. ## 决策记录 / Decisions - 章节内显示 = 按容器宽度(≤720px)连续滚动、无 snap、无适高一屏一页。用户选定(方案 A)。 - 翻页手势/按键 = 章内翻一屏(`scrollBy ±vh`),不再按图跳页。用户选定。 - 采用「按章节作用域渲染」(方案 A),否决:全书渲染+滚动钳制(手感生硬、浪费加载)、 一章一挂载(书签/进度/量高学习跨章迁移复杂、切章闪烁)。 - 预读实现为隐藏层挂载解码(不增加可滚动高度),不违反锁章语义。用户要求「可预渲染下两章、可配置」。 - 进度滑条/页码/书签/服务端进度均仍为全书页号,locator 形状 `{page}` 不变,无后端/API 改动。 - 扁平包(chapters 为 null)= 整本一章:章按钮与连读开关隐藏,锁章/预读无意义,行为即纯连续滚动。 ## 前端 / Frontend(全部改动在 `frontend/src`,纯前端) ### 状态与持久化 - 删除 `PageMode`/`MODES`/`MODE_LABEL`/`useStoredMode` 及 `cbz-mode` 键;PageImg 去掉 `fitH` 分支; 滚动盒去掉 `snap-y snap-proximity`。 - `localStorage("cbz-continuous")`:连读开关,默认关(锁章)。 - `localStorage("cbz-prefetch")`:预读章数 0–3,默认 2。工具栏步进器按钮循环 0→1→2→3→0。 ### 区间几何 - 当前章 `ci` 由滚动位置(现 `cur` 所在页 → 章)推出,章起点 `chapters[ci].start`。 - `PageHeights` 仍覆盖全书 `count` 页(复用现有量高/估高/`applyShift` 补偿逻辑)。 - 挂载上限 `mountEnd`:锁章 = 第 `ci+1+prefetch` 章起点(越界取 count);连读 = count。 - 滚动终点 `scrollEnd`:锁章 = `ph.offset(章末) + 章末卡片高`;连读 = 全书末尾。 - 超出 `scrollEnd` 的预读页渲染进一个 `height:0; overflow:hidden` 的绝对定位隐藏层 (子页仍按 `ph.offset(i)` 定位、`aria-hidden`):blob 经 `fetchObjectUrl` 缓存、`` 挂载即解码, 但不参与滚动几何。切章后这些页落入可见区间即直接上屏。 - 纯计算抽为 `lib/virt.ts` 导出 helper(如 `chapterRange(chapters, count, ci, continuous, prefetch)` → `{mountStart, mountEnd, scrollEndPage}`),vitest 覆盖。 ### 交互 - 点按左右侧区/←→/PageUp·Down:`scrollBy(±vh, smooth)`;锁章时滚动高度天然停在章末卡片。 - 点中间仍唤出工具栏(不变)。 - 章末卡片:锁章时本章末尾一屏「本章完 · 下一章 →」(最终章显示「全书完」),点卡片=切章。 - 切章入口(上一章/下一章按钮、目录行、书签 seek、章末卡片、滑条拖入他章区间): 设 `ci` 并 `jump(page)`;滑条跨章视为显式定位,不算「滑动越界」。 - 恢复进度:由 `initialLocator.page` 反推所在章后定位。 - 闲时「停滚 250ms 预取后 5 页」保留。 ## 错误处理 / Errors - 无新增失败面:页数接口、鉴权取图、失败重试(「本页加载失败」)均沿用。 - `cbz-prefetch` 值非法(手改 localStorage)→ 回退默认 2;`cbz-continuous` 非 `"1"` → 关。 ## 测试 / Tests - `lib/virt.ts` helper 表驱动单测:锁/连读 × 预读 0–3 × 首章/末章/单章扁平包 × 边界页; 断言 mountEnd/scrollEnd 不越过滚动锁、预读不超过 count。 - 现有 `virt` 用例保持绿;`npm run check` 绿。 - 真实浏览器点验:锁章滚不到下章、开连读可滑过、预读下切章无白屏、扁平包无章 UI。 ## 文档 / Docs - `docs/CHANGELOG_web.md` 新增条目(英中各一行),不重复进 `docs/CHANGELOG.md`。 ## 不做 / Non-goals - 后端章节结构改动、章内页号重映射、PDF/EPUB/TXT 阅读器改动、横翻 RTL 模式、 长按拖拽越章「窥视」下一章。