mirror of
https://github.com/hectorqin/reader.git
synced 2026-11-13 22:41:06 +08:00
Merge remote-tracking branch 'origin/main' into auto/library-browse-files-split-2e69
把 #48 的朗读条与快捷列改动合进本分支,两处冲突都在 `web/src/ui/reader-chrome.tsx`,都取 main 那一侧: - 快捷列:main 删掉了 目录 / 行距 / 页边距 / 设置 四个入口(#48 的报告), 本分支新增的 `menu` / `line-height` / `indent` / `sliders` 随之作废。 留下 主题 / 字号 / 听书 三项,字号改用本分支新词汇表的 `text-size`, 听书改用 `volume`。 - 朗读条:main 拆成两行(控制行 + 进度行)并加了 `tts-stop`。本分支的单行 版本丢弃。图标名按本分支的词汇表回落:`backward-step`/`forward-step` → `step-backward`/`step-forward`,`xmark` → `close`。 为让两边都能编译,`icon-names.ts` 增加一层别名表 `ICON_ALIASES` (`font`/`volume-high`/`bars`/`gear`/`xmark`/`magnifying-glass`/`books` → 已画的码点),另导出 `ICON_CODEPOINT_TABLE`。别名是**拼写**不是字形: `icons.test.ts` 的借用名单断言只管 `paths.mjs`,新增两条断言钉住别名既 落在被否名单里、又必须指向字体真画了的码点;码点唯一性改按去重后的集合计。
This commit is contained in:
20 files changed
+1350
-273
No files matched your search
@@ -723,6 +723,23 @@ Android WebView 是真实会发生的)才推进,不是为了给朗读计时
|
||||
第一版就是这么做的,结果是目录面板显示「第 1 章 – 第 40 章」,服务端的分页边界
|
||||
漏进了 UI。
|
||||
|
||||
### 书自己的目录页也是一个章节,它的链接要当章节切换
|
||||
|
||||
一本 EPUB 的 `nav`/`toc` 文档是**普通的 spine 条目**,读者翻到它、点「第三章」,
|
||||
要的是换章。但服务端把章节里的相对引用都改写成了 assets 端点的绝对 URL
|
||||
(否则相对 href 从归档外面根本解析不到),于是那个 `<a href>` 默认是一个
|
||||
**资源请求**:浏览器去下载另一章的文档,读者看到的不是第三章,而是一张坏掉的页面。
|
||||
|
||||
客户端因此要在**点击**这一层把它读回来:`chapterRefFromLink()` 认出
|
||||
`__reader-book-resource__=1&ref=OEBPS/ch3.xhtml` 里的 `ref`,拼成 manifest 的
|
||||
`xhtml:<path>`——和目录面板给的是同一种引用,所以它走的是**同一条**跳转路径
|
||||
(`goToChapterRef`),跨窗口取窗也一样。
|
||||
|
||||
两条附带的规则同样重要:识别不出来的链接**不放行**(一本书里的站外链接不能让
|
||||
读者离开阅读器,WebView 壳里更是回不来),而**纯片段链接**(`#note3`)交给浏览器
|
||||
自己跳——脚注必须能用。边界写在 `ReaderView.bindChapterLinks` 里,因为它是唯一
|
||||
同时看得见 shadow 树里真实节点和网络地址的地方。
|
||||
|
||||
### 章节的读取代价
|
||||
|
||||
一次章节读取 = 一次中央目录读(打开时)+ 一次条目的局部头 + 该条目的数据。
|
||||
|
||||
+22
-19
@@ -26,40 +26,43 @@
|
||||
|
||||
| 检查 | 结果 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 展开工具栏: 快捷按钮与两条栏一起出现 | ✅ | visible=true 按钮 7 个 pointer-events=auto |
|
||||
| 快捷列: 整组控件都在页内 | ✅ | 列 y=186..534 / 屏高 844 |
|
||||
| 展开工具栏: 快捷按钮与两条栏一起出现 | ✅ | visible=true 按钮 3 个 pointer-events=auto |
|
||||
| 快捷列: 整组控件都在页内 | ✅ | 列 y=186..335 / 屏高 844 |
|
||||
| 快捷列: 装不下的控件有滚动条,说明还有更多 | ✅ | 溢出 否 overflow-y=true 滚动条 0px |
|
||||
| 快捷列: 一列里含整组快捷操作,而不是只露出前几个 | ✅ | 按钮 7 个:目录 / 日间 / 字号 / 行距 / 页边距 / 听书 / 阅读设置 |
|
||||
| 快捷列: 听书在列内 | ✅ | 列内控件:目录 / 日间 / 字号 / 行距 / 页边距 / 听书 / 阅读设置 |
|
||||
| 快捷列(420px 短屏): 列在页内,超出的控件能滚到 | ✅ | 列底 322 / 屏高 420,滚动条 0px,滚到底后末个控件可见=true |
|
||||
| 快捷列: 只留页面内就能完成的调整 | ✅ | 按钮 3 个:日间 / 字号 / 听书 |
|
||||
| 快捷列: 不含打开面板的五个入口 | ✅ | 列内控件:日间 / 字号 / 听书 |
|
||||
| 快捷列: 听书在列内 | ✅ | 列内控件:日间 / 字号 / 听书 |
|
||||
| 快捷列(420px 短屏): 列在页内,超出的控件能滚到 | ✅ | 列底 242 / 屏高 420,滚动条 0px,滚到底后末个控件可见=true |
|
||||
| 收起工具栏: 顶栏与底栏一起走 | ✅ | state=hidden 顶栏 hidden / 底栏 hidden |
|
||||
| 收起工具栏: 两条栏都不可见,也不可点 | ✅ | topbar=none footer=none |
|
||||
| 收起工具栏: 正文一行的位置不变(工具栏是浮层) | ✅ | 正文首行 y: 显示时 78 / 收起后 78 |
|
||||
| 收起工具栏: 正文一行的位置不变(工具栏是浮层) | ✅ | 正文首行 y: 显示时 24 / 收起后 24 |
|
||||
| 正文铺满: 页面不为工具栏预留高度 | ✅ | padding 0/0px, 页面 844px / 屏高 844 |
|
||||
| 工具栏是浮动层,不占正文的高度 | ✅ | topbar=absolute footer=absolute |
|
||||
| 收起工具栏: 快捷按钮跟着两条栏一起收 | ✅ | visible=false pointer-events=none |
|
||||
| 收起工具栏: 保留章节与页码指示 | ✅ | chapter="第一章 惊蛰" progress="第 1/4 页 0%" |
|
||||
| 收起工具栏: 保留章节与页码指示 | ✅ | chapter="第一章 惊蛰" progress="第 1/3 页 0%" |
|
||||
| 收起工具栏: 阅读指示不吃手势 | ✅ | pointer-events=none |
|
||||
| 收起工具栏: 章节名画在正文之上 | ✅ | 层级 30 vs 正文 1,y=10..28 文本="第一章 惊蛰" |
|
||||
| 收起工具栏: 章节名自带底色,不被正文透出 | ✅ | background=rgb(22, 23, 26) |
|
||||
| 收起工具栏: 页码读数画在正文之上 | ✅ | 层级 30 vs 正文 1,y=819..834 文本="第 1/4 页 0%" |
|
||||
| 收起工具栏: 页码读数画在正文之上 | ✅ | 层级 30 vs 正文 1,y=819..834 文本="第 1/3 页 0%" |
|
||||
| 收起工具栏: 页码读数自带底色,不被正文透出 | ✅ | background=rgb(22, 23, 26) |
|
||||
| 收起工具栏: 章节名与页码左边对齐 | ✅ | 章节名 left=20 页码 left=20 |
|
||||
| 点右侧翻页: 既翻页又把工具栏叫回来 | ✅ | 收起前 hidden(第 1/4 页) → 点击后 visible(第 2/4 页) |
|
||||
| 目录: 面板不遮住整页 | ✅ | 面板从 y=321 开始,正文区 y=54 |
|
||||
| 目录: 面板留出可预览的正文 | ✅ | 正文可见高度 266px |
|
||||
| 点右侧只翻页: 页码前进, 工具栏保持收起 | ✅ | 收起前 hidden(第 1/3 页) → 点击后 hidden(第 2/3 页) |
|
||||
| 点中间切换工具栏 | ✅ | 点击中间后 visible |
|
||||
| 目录: 面板不遮住整页 | ✅ | 面板从 y=321 开始,正文区 y=0 |
|
||||
| 目录: 面板留出可预览的正文 | ✅ | 正文可见高度 321px |
|
||||
| 目录: 面板有遮罩和把手 | ✅ | scrim=true grip=true |
|
||||
| 目录: 面板高度不超过阅读区 | ✅ | 面板 523px / 阅读区 700px |
|
||||
| 目录: 面板高度不超过阅读区 | ✅ | 面板 523px / 阅读区 844px |
|
||||
| 顶栏: 三个按钮左右两边对齐 | ✅ | 左 12px / 右 12px,两道间隙 136px / 136px |
|
||||
| 底栏: 页码与下一章右边对齐,滑杆与上一章左边对齐 | ✅ | 页码右 12px / 下一章右 12px;上一章左 12px / 滑杆左 12px |
|
||||
| 顶栏与底栏共用同一条内边距 | ✅ | 顶栏 12px / 底栏 12px |
|
||||
| 底栏: 进度是可拖动的滑杆 | ✅ | type=range 宽 308px label="章节内页数" |
|
||||
| 底栏: 有页码与阅读进度读数 | ✅ | page="第 2/4 页" nav="阅读进度:27%" |
|
||||
| 底栏: 滑杆量的是章节内页数,不是全书百分比 | ✅ | min=1 max=4 value=2 读数="第 2/4 页"(章节共 4 页) |
|
||||
| 底栏: 拖动滑杆切到对应页 | ✅ | 拖到 4 后读数="第 4/4 页" |
|
||||
| 翻页模式: 滑杆拖到第 3 页,仍在本章 | ✅ | min=1 max=4 value=3 拖前="第 4/4 页" 拖后="第 3/4 页" |
|
||||
| 状态提示不推动正文 | ✅ | 正文顶部 y: 54.390625 / 54.390625 / 54.390625 |
|
||||
| 触控目标不小于 28px | ✅ | 12 个控件全部合格 |
|
||||
| 每个控件都有可读的名字 | ✅ | 12 个控件全部有名字 |
|
||||
| 底栏: 有页码与阅读进度读数 | ✅ | page="第 2/3 页" nav="阅读进度:41%" |
|
||||
| 底栏: 滑杆量的是章节内页数,不是全书百分比 | ✅ | min=1 max=3 value=2 读数="第 2/3 页"(章节共 3 页) |
|
||||
| 底栏: 拖动滑杆切到对应页 | ✅ | 拖到 3 后读数="第 3/3 页" |
|
||||
| 翻页模式: 滑杆拖到第 3 页,仍在本章 | ✅ | min=1 max=3 value=3 拖前="第 3/3 页" 拖后="第 3/3 页" |
|
||||
| 状态提示不推动正文 | ✅ | 正文顶部 y: 0 / 0 / 0 |
|
||||
| 触控目标不小于 28px | ✅ | 8 个控件全部合格 |
|
||||
| 每个控件都有可读的名字 | ✅ | 8 个控件全部有名字 |
|
||||
| 没有横向溢出 | ✅ | document 溢出 0px |
|
||||
| 翻页不再每次都发两次 sync | ✅ | 8 次翻页 + 6 秒静默:1 次 POST / 1 次 GET(共 2 次 sync 请求) |
|
||||
| EPUB: 章节里的图片真的画出来了 | ✅ | naturalWidth=240 显示 240x160 src="/api/v1/books/review-illustrated/assets?__reader-book-resource__=1&ref=OEBPS%2FI" |
|
||||
|
||||
+36
-21
@@ -89,12 +89,12 @@
|
||||
{
|
||||
"name": "展开工具栏: 快捷按钮与两条栏一起出现",
|
||||
"ok": true,
|
||||
"detail": "visible=true 按钮 7 个 pointer-events=auto"
|
||||
"detail": "visible=true 按钮 3 个 pointer-events=auto"
|
||||
},
|
||||
{
|
||||
"name": "快捷列: 整组控件都在页内",
|
||||
"ok": true,
|
||||
"detail": "列 y=186..534 / 屏高 844"
|
||||
"detail": "列 y=186..335 / 屏高 844"
|
||||
},
|
||||
{
|
||||
"name": "快捷列: 装不下的控件有滚动条,说明还有更多",
|
||||
@@ -102,19 +102,24 @@
|
||||
"detail": "溢出 否 overflow-y=true 滚动条 0px"
|
||||
},
|
||||
{
|
||||
"name": "快捷列: 一列里含整组快捷操作,而不是只露出前几个",
|
||||
"name": "快捷列: 只留页面内就能完成的调整",
|
||||
"ok": true,
|
||||
"detail": "按钮 7 个:目录 / 日间 / 字号 / 行距 / 页边距 / 听书 / 阅读设置"
|
||||
"detail": "按钮 3 个:日间 / 字号 / 听书"
|
||||
},
|
||||
{
|
||||
"name": "快捷列: 不含打开面板的五个入口",
|
||||
"ok": true,
|
||||
"detail": "列内控件:日间 / 字号 / 听书"
|
||||
},
|
||||
{
|
||||
"name": "快捷列: 听书在列内",
|
||||
"ok": true,
|
||||
"detail": "列内控件:目录 / 日间 / 字号 / 行距 / 页边距 / 听书 / 阅读设置"
|
||||
"detail": "列内控件:日间 / 字号 / 听书"
|
||||
},
|
||||
{
|
||||
"name": "快捷列(420px 短屏): 列在页内,超出的控件能滚到",
|
||||
"ok": true,
|
||||
"detail": "列底 322 / 屏高 420,滚动条 0px,滚到底后末个控件可见=true"
|
||||
"detail": "列底 242 / 屏高 420,滚动条 0px,滚到底后末个控件可见=true"
|
||||
},
|
||||
{
|
||||
"name": "收起工具栏: 顶栏与底栏一起走",
|
||||
@@ -129,7 +134,12 @@
|
||||
{
|
||||
"name": "收起工具栏: 正文一行的位置不变(工具栏是浮层)",
|
||||
"ok": true,
|
||||
"detail": "正文首行 y: 显示时 78 / 收起后 78"
|
||||
"detail": "正文首行 y: 显示时 24 / 收起后 24"
|
||||
},
|
||||
{
|
||||
"name": "正文铺满: 页面不为工具栏预留高度",
|
||||
"ok": true,
|
||||
"detail": "padding 0/0px, 页面 844px / 屏高 844"
|
||||
},
|
||||
{
|
||||
"name": "工具栏是浮动层,不占正文的高度",
|
||||
@@ -144,7 +154,7 @@
|
||||
{
|
||||
"name": "收起工具栏: 保留章节与页码指示",
|
||||
"ok": true,
|
||||
"detail": "chapter=\"第一章 惊蛰\" progress=\"第 1/4 页 0%\""
|
||||
"detail": "chapter=\"第一章 惊蛰\" progress=\"第 1/3 页 0%\""
|
||||
},
|
||||
{
|
||||
"name": "收起工具栏: 阅读指示不吃手势",
|
||||
@@ -164,7 +174,7 @@
|
||||
{
|
||||
"name": "收起工具栏: 页码读数画在正文之上",
|
||||
"ok": true,
|
||||
"detail": "层级 30 vs 正文 1,y=819..834 文本=\"第 1/4 页 0%\""
|
||||
"detail": "层级 30 vs 正文 1,y=819..834 文本=\"第 1/3 页 0%\""
|
||||
},
|
||||
{
|
||||
"name": "收起工具栏: 页码读数自带底色,不被正文透出",
|
||||
@@ -177,19 +187,24 @@
|
||||
"detail": "章节名 left=20 页码 left=20"
|
||||
},
|
||||
{
|
||||
"name": "点右侧翻页: 既翻页又把工具栏叫回来",
|
||||
"name": "点右侧只翻页: 页码前进, 工具栏保持收起",
|
||||
"ok": true,
|
||||
"detail": "收起前 hidden(第 1/4 页) → 点击后 visible(第 2/4 页)"
|
||||
"detail": "收起前 hidden(第 1/3 页) → 点击后 hidden(第 2/3 页)"
|
||||
},
|
||||
{
|
||||
"name": "点中间切换工具栏",
|
||||
"ok": true,
|
||||
"detail": "点击中间后 visible"
|
||||
},
|
||||
{
|
||||
"name": "目录: 面板不遮住整页",
|
||||
"ok": true,
|
||||
"detail": "面板从 y=321 开始,正文区 y=54"
|
||||
"detail": "面板从 y=321 开始,正文区 y=0"
|
||||
},
|
||||
{
|
||||
"name": "目录: 面板留出可预览的正文",
|
||||
"ok": true,
|
||||
"detail": "正文可见高度 266px"
|
||||
"detail": "正文可见高度 321px"
|
||||
},
|
||||
{
|
||||
"name": "目录: 面板有遮罩和把手",
|
||||
@@ -199,7 +214,7 @@
|
||||
{
|
||||
"name": "目录: 面板高度不超过阅读区",
|
||||
"ok": true,
|
||||
"detail": "面板 523px / 阅读区 700px"
|
||||
"detail": "面板 523px / 阅读区 844px"
|
||||
},
|
||||
{
|
||||
"name": "顶栏: 三个按钮左右两边对齐",
|
||||
@@ -224,37 +239,37 @@
|
||||
{
|
||||
"name": "底栏: 有页码与阅读进度读数",
|
||||
"ok": true,
|
||||
"detail": "page=\"第 2/4 页\" nav=\"阅读进度:27%\""
|
||||
"detail": "page=\"第 2/3 页\" nav=\"阅读进度:41%\""
|
||||
},
|
||||
{
|
||||
"name": "底栏: 滑杆量的是章节内页数,不是全书百分比",
|
||||
"ok": true,
|
||||
"detail": "min=1 max=4 value=2 读数=\"第 2/4 页\"(章节共 4 页)"
|
||||
"detail": "min=1 max=3 value=2 读数=\"第 2/3 页\"(章节共 3 页)"
|
||||
},
|
||||
{
|
||||
"name": "底栏: 拖动滑杆切到对应页",
|
||||
"ok": true,
|
||||
"detail": "拖到 4 后读数=\"第 4/4 页\""
|
||||
"detail": "拖到 3 后读数=\"第 3/3 页\""
|
||||
},
|
||||
{
|
||||
"name": "翻页模式: 滑杆拖到第 3 页,仍在本章",
|
||||
"ok": true,
|
||||
"detail": "min=1 max=4 value=3 拖前=\"第 4/4 页\" 拖后=\"第 3/4 页\""
|
||||
"detail": "min=1 max=3 value=3 拖前=\"第 3/3 页\" 拖后=\"第 3/3 页\""
|
||||
},
|
||||
{
|
||||
"name": "状态提示不推动正文",
|
||||
"ok": true,
|
||||
"detail": "正文顶部 y: 54.390625 / 54.390625 / 54.390625"
|
||||
"detail": "正文顶部 y: 0 / 0 / 0"
|
||||
},
|
||||
{
|
||||
"name": "触控目标不小于 28px",
|
||||
"ok": true,
|
||||
"detail": "12 个控件全部合格"
|
||||
"detail": "8 个控件全部合格"
|
||||
},
|
||||
{
|
||||
"name": "每个控件都有可读的名字",
|
||||
"ok": true,
|
||||
"detail": "12 个控件全部有名字"
|
||||
"detail": "8 个控件全部有名字"
|
||||
},
|
||||
{
|
||||
"name": "没有横向溢出",
|
||||
|
||||
+62
-24
@@ -656,12 +656,17 @@ TXT 是唯一一种**没有作者排版**的书,所以它也是唯一一处「
|
||||
内容会上下滑动」:当时两条栏在正常流里,stage 是 `flex: 1 1 auto`,于是每次
|
||||
开关工具栏页面就重新排版、正在读的那一行上下滑 53px。工具栏是浮层,和正文不在
|
||||
同一个流里——读者的判断是对的。
|
||||
- **正文给浮层留的是固定边距**(`.stage-host` 的 `padding-block:
|
||||
var(--reader-chrome-top) var(--reader-chrome-bottom)`)。浮层会盖住页面,
|
||||
所以版心必须让开两条栏的高度;而「固定」是这里的关键词:这个边距**不随工具栏
|
||||
状态变化**,否则读者一点按钮页面就重新排版,那是同一个 bug 换了件衣服。
|
||||
两个高度各自只声明一次(`.reader-screen` 上的 `--reader-chrome-top` /
|
||||
`--reader-chrome-bottom`),栏本身和让位规则读同一个数,不会各说各话。
|
||||
- **页面铺满整屏,不为浮层预留任何高度。** `.stage-host` 是 `inset: 0`,
|
||||
**没有** `padding-block`。这是读者报的「内容要铺满,不要预留 stage」:版心
|
||||
曾经按两条栏的高度留固定边距,于是页面在**两种状态下**都永久比手机矮 9rem
|
||||
——读者收起了工具栏、要的就是整屏,顶上顶下却还是两块空白纸。
|
||||
留边距的理由(浮层会盖住正文)本身没错,所以浮层的解法是浮层自己的:两条栏
|
||||
用半透明底加 `backdrop-filter`(`--reader-surface-float`),读者能**透过**
|
||||
工具栏看到它盖住的那一行。`.book-flow` 自己的 `padding-block` 是**页面**
|
||||
边距,上下对称、与工具栏无关,一直在。
|
||||
- **两条栏的高度各自只声明一次**(`.reader-screen` 上的 `--reader-chrome-top` /
|
||||
`--reader-chrome-bottom`):栏本身、以及底栏上方那条朗读条(`.tts-bar` 的
|
||||
`bottom`)读同一个数,不会各说各话。
|
||||
- **`visibility` 在动画结束后才生效**(`transition: visibility 0s linear
|
||||
var(--motion-duration)`),过渡期间由 `pointer-events: none` 兜住:滑出的过程
|
||||
中标题还在,动画走完它就既看不见也点不到了。少写这一行,读者能点到一个看不见的
|
||||
@@ -671,15 +676,16 @@ TXT 是唯一一种**没有作者排版**的书,所以它也是唯一一处「
|
||||
(宽屏时用 `--reader-measure` 算出的天头地脚里),不压字。它**不属于沉浸态**:
|
||||
这些动作和顶栏是同一批目的地,属于「读者要控件」这个状态;沉浸态多出一列浮在
|
||||
正文右边缘的按钮,就不是读者要的「只剩正文」。
|
||||
- **快捷列装的是整组调整,而不是其中的几个**:目录 / 主题 / 字号 / 行距 / 页边距 /
|
||||
听书 / 设置。每个按钮是该项设置的**一档**,值和设置面板里的行取自**同一张
|
||||
阶梯**(`LINE_HEIGHT_LADDER`、`PAGE_MARGIN_*`),所以两处不会各自漂移出一个
|
||||
对方到不了的值。
|
||||
这条是读者报的「竖排工具栏显示为 主题切换、听书」逼出来的:当时这一列
|
||||
**没有高度上限也不会滚**,屏幕一矮就直接画出屏幕外——读者数得到几个就以为这一列
|
||||
只有几个,于是报告了他看得见的那两个。所以有三条实现约束:
|
||||
`max-height` 由两条栏的高度算出来(和正文让开的是同两个数),装不下时
|
||||
`overflow-y: auto`,并且**滚动条是画出来的**(3px 细线而不是
|
||||
- **快捷列只留页面内就能完成的调整**:夜间/日间(主题)、字号、听书。
|
||||
这条是读者报的「竖排工具栏去掉 目录、字体、行距、边距、设置这几个按钮」定下来的:
|
||||
被去掉的五个都是**目的地**而不是**调整**——目录、设置各自打开半屏面板,字体/行距/
|
||||
页边距是值的一档而其完整范围住在设置面板里——所以这一列同时也装着它们的时候,
|
||||
它就是一列浮在正文上的、更差的设置面板。留下的是读者**不想打开任何东西**就会去动的
|
||||
那几个(尤其听书:他伸手的时候正要离开屏幕)。
|
||||
每个按钮仍然是该项设置的一档,值和设置面板里的行取自**同一张阶梯**
|
||||
(`FONT_SCALE_LADDER`、`THEME_LADDER`),所以两处不会各自漂移出一个对方到不了的值。
|
||||
列本身仍要保证整组控件在页内:`max-height` 由两条栏的高度算出来,
|
||||
装不下时 `overflow-y: auto`,并且**滚动条是画出来的**(3px 细线而不是
|
||||
`scrollbar-width: none`)——一个只能靠滚动找到的控件必须看起来能滚,否则
|
||||
对读者就等于不存在。`overscroll-behavior: contain` 也在,免得滑到头那一下把
|
||||
手势交给下面的正文。
|
||||
@@ -709,16 +715,43 @@ TXT 是唯一一种**没有作者排版**的书,所以它也是唯一一处「
|
||||
所以拖动滑杆**不会跨章**:它只移动当前章节内的位置(见
|
||||
`ReaderView.seekPageInChapter`)。
|
||||
|
||||
手势上,**中间三分之一在两个方向上都是开关**;**左右三分之一翻页,且收起状态下
|
||||
顺带把工具栏叫回来**。
|
||||
手势上,**三个区各管一件事,互不重叠**:左右三分之一**只翻页**,中间三分之一
|
||||
**只切换工具栏显隐**。
|
||||
|
||||
这一条改过,起因是读者报的「点击左右侧翻页时工具栏不能显示出来」。原来的行为是:
|
||||
收起状态下左右三分之一**不翻页**,只当作「叫回来」的出口(理由是没有别的出路)。
|
||||
理由成立,结果不成立——读者点右侧是想看下一页,得到的却是没有下一页、外加一个他
|
||||
没在找的工具栏;于是同一根手指同时说出了「翻页没反应」和「工具栏不显示」两句抱怨。
|
||||
现在这一次点击两件事都做:翻页,并且把工具栏叫回来。两者本来就不是二选一。
|
||||
工具栏**已经显示时**,左右三分之一仍然只翻页:那才是 §3.8 说的「把翻页没反应做成
|
||||
常态」,而且每翻一页闪一下两条栏是立刻会被注意到的毛病。
|
||||
这一条改过两轮,第二轮的起因是读者报的「在点击左右两边时只需要翻页、不需要显示
|
||||
工具栏,只有在中间点击时才需要切换工具栏显隐」。第一轮的行为是:收起状态下左右
|
||||
三分之一翻页**并且**把工具栏叫回来(理由是没有别的出路)。理由成立,结果和手势
|
||||
打架——读者只是想往后翻,得到的却是新的一页**外加**一条压在刚读到的那行上的工具栏,
|
||||
于是沉浸态根本待不住,每翻一页都被撤销一次;而真正想要控件的读者有一个专门的、
|
||||
一眼可见的表达方式:点中间。
|
||||
所以现在两个手势是**互斥**的,各有一个含义,这正是原来缺的性质:
|
||||
左/右 = 翻页,中 = 显隐工具栏。翻页不会带出任何控件,要控件的点击也不会翻页。
|
||||
|
||||
### 3.11 朗读条
|
||||
|
||||
朗读条(`.tts-bar`)**和两条栏一样是浮层**,贴在底栏正上方(`bottom:
|
||||
var(--reader-chrome-bottom)`),z-index 用同一个 `--reader-layer-bar`;但它
|
||||
**不跟工具栏一起收起**——读者是边听边走的人,收起工具栏是为了看正文,不是为了
|
||||
控制不了播放。
|
||||
|
||||
这条是读者报的「TTS 阅读体验需要优化,没法停止」逼出来的两条实现约束:
|
||||
|
||||
- **它必须画在正文之上,才算存在。** 它原本是 `.reader-screen` 这个 flex 列的
|
||||
普通子元素,而第一个子元素 `.stage-host` 是 `inset: 0` 的绝对定位、正文又画在
|
||||
`--reader-layer-page`。静态兄弟排在浮层**下面**:条画出来了、按钮能聚焦、
|
||||
`aria-label` 也对,**没有任何一个能被点到**。所以它显式声明 `position: absolute`
|
||||
加 z-index——一个实心按钮点不到,读者只有一个说法:「没法停止」。
|
||||
- **控件自己一行,正文和滑杆另起一行。** 原本七个东西挤在一行里,句子拿
|
||||
`flex: 1`,于是它成了唯一会被压缩的东西,而最后一个(停止)在 360px 上被推出
|
||||
右边缘裁掉。现在上行是 `flex: 1 1 0` 的五个等宽档(上一句 / 播放暂停 / 下一句 /
|
||||
句数 chip / 停止),下行是句子加滑杆:控件的宽度是等分出来的,屏幕多宽都放得下。
|
||||
- **停止是方块不是 ✕。** ✕ 读起来是「关掉这条」,而那正是暂停不做的事——想要
|
||||
声音停下的读者按了它,条消失了,书还在念。停止单独用主色底(`.tts-stop`)标出,
|
||||
它是一行五个控件里唯一一个破坏性的。
|
||||
|
||||
播放器元素本身(`<audio>`)**永远不进文档**,并且显式带 `display: none` 与
|
||||
`aria-hidden`:媒体元素没有读者想要的控件(条上都有),而一个会渲染它的 WebView
|
||||
只会在书上画一个黑色播放器方块。这是读者报的「audio 需要隐藏」。
|
||||
|
||||
---
|
||||
|
||||
@@ -784,6 +817,11 @@ TXT 是唯一一种**没有作者排版**的书,所以它也是唯一一处「
|
||||
| 图标按钮自己拼的 | ✅ 点击有反应 | 图标和文字差半个字高 |
|
||||
| 底栏滑杆量的是全书百分比 | ✅ 拖了会动、页码也变了 | 想翻到本章第 7 页,被带到了第 40 章 |
|
||||
| 快捷按钮列只在沉浸态出现 | ✅ 收起工具栏后按钮还在 | 收起后正文右边仍压着一列浮层 |
|
||||
| 朗读条是 flex 里的一行 | ✅ 按钮渲染出来了、`aria-label` 也在 | 它是静态兄弟、被 `inset: 0` 的 stage 盖住,实心按钮一个都点不到 |
|
||||
| 朗读条的控件挤在一行里 | ✅ 每个控件单独看都在 DOM 里 | 手机宽度下最后一个(停止)被挤出屏幕外,那就是「没法停止」 |
|
||||
| 快捷列装的是打开面板的入口 | ✅ 每个按钮都能点开东西 | 浮在正文上的第二份设置面板,比面板本身更难用 |
|
||||
| 版心按工具栏高度留固定边距 | ✅ 开关工具栏时正文一行不动 | 两种状态下页面都矮 9rem,收起了工具栏也还是两块空白纸 |
|
||||
| 分栏的 column-gap 是 0、页边距是内边距 | ✅ 滑杆能拖、页码会变 | 每页右边缘露出下一页的头几个字,翻页「样式不对」 |
|
||||
|
||||
所以 `npm run ui:review` 做两件事,两件都要:
|
||||
|
||||
|
||||
Reference in new issue
Block a user