Files
kindle-calendar/dash/docs/layered-clock-plan.zh.md
douboer@gmail.com 3d19c4d34f first commit
2026-03-15 09:30:40 +08:00

450 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Kindle Dashboard 分层时钟方案
## 1. 背景
当前仓库里有两部分:
- `calendar/`:负责渲染仪表盘网页与导出背景素材
- `dash/`:运行在 Kindle 上,负责拉图、刷屏、休眠与唤醒
最新设计稿来自 Figma
- 文件:`calendar`
- 节点:`6:2`
- 链接:`https://www.figma.com/design/3bXFNM5nM6mCq0TpL3nPYK/calendar?node-id=6-2&m=dev`
该节点 annotation 已明确:
- `阳历当天`
- `星期`
- `农历日`
- `时钟区域`
- `日历`
- `天气预报卡片`
- `书摘卡片`
本方案基于这些约束,采用“低频背景 + 本地时钟”的拆分方式:
- `calendar/``2 小时` 生成一次背景图
- 背景图直接写到:
- `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png`
- Kindle 通过固定 HTTPS 地址拉取背景图:
- `https://shell.biboer.cn:20001/kindlebg.png`
- Kindle 端每分钟`不联网`
- Kindle 端只在本地重画时钟区域
## 2. 目标
这次改造不是单纯调样式,而是把页面拆成两类刷新节奏完全不同的素材:
1. 低频背景层
2. 高频时钟层
目标收益:
- 背景内容不再分钟级刷新
- 分钟级变化只限制在时钟区域
- 降低 Wi-Fi 唤醒次数
- 降低全屏刷新频率
- 减少墨水屏闪烁与残影
## 3. 分层边界
### 3.1 全屏背景层
背景层文件名固定为:
- `kindlebg.png`
背景层包含:
- 整体外层容器、圆角、阴影、背景渐变
- 日历卡片中的:
- 阳历当天数字
- 星期
- 农历日
- 下方月历区域
- 天气卡片全部内容
- 书摘卡片全部内容
背景层**不包含**
- 时钟表盘主体
- 时钟刻度
- 时针
- 分针
- 中心圆点
也就是说,背景图在时钟区域只保留布局占位,不直接承载任何分钟级内容。
### 3.2 静态表盘 patch
静态表盘 patch 为一个独立本地素材:
- `clock-face.png`
该素材包含:
- 圆形表盘主体
- 刻度
- 中心底座或中心圆盘
建议尺寸直接对齐 Figma 时钟区域:
- 节点:`24:74`
- 设计尺寸:`220 x 220`
这张图应当保存在 Kindle 本地,例如:
- `/mnt/us/dashboard/assets/clock-face.png`
它不需要分钟级联网拉取,只需要在部署时同步到设备,或者在更换设计稿时重新同步。
### 3.3 指针层
高频变化层只包含:
- 时针
- 分针
这里不建议包含秒针:
- 墨水屏收益低
- 刷新频率会显著上升
- 更容易产生残影
由于当前 `dash/` 链路本质是 `eips` 刷图,不是 SVG/Canvas 实时渲染,所以推荐使用`分层素材方案`
- `minute-hand/00.png``minute-hand/59.png`
- `hour-hand/000.png``hour-hand/719.png`
说明:
- 分针每分钟一个角度,共 `60`
- 时针如果要做到真正随分钟连续移动,需要 `12 * 60 = 720`
- 这些都是小尺寸 patch不是整屏图体积可控
如果后续确认 Kindle 端存在稳定的本地绘线工具,再考虑把指针改成算法绘制;当前版本不依赖这个前提。
## 4. 为什么不能只把表盘放进整页背景
这个问题必须单独说明。
如果:
- 表盘主体和刻度只存在于 `kindlebg.png`
- Kindle 每分钟只覆盖新的时针/分针
那么上一分钟留下的旧指针就无法被干净擦除。
因此 Kindle 端每分钟的正确流程应该是:
1. 先重画一张本地 `clock-face.png`
2. 再叠加新的时针素材
3. 再叠加新的分针素材
这等价于“先擦除,再重画”,并且整个流程不依赖网络。
所以本方案不是“背景图 + 两根指针”两层,而是:
1. `kindlebg.png`:全屏低频背景
2. `clock-face.png`:本地静态表盘 patch
3. `hour-hand/*.png + minute-hand/*.png`:本地高频指针素材
## 5. 数据刷新策略
### 5.1 背景层刷新
背景层刷新触发条件:
-`2 小时` 一次
- 跨天时立即刷新一次
- 天气接口异常恢复后可补刷一次
推荐调度:
- `00:00 / 02:00 / 04:00 / ... / 22:00`
背景层刷新输出:
- `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png`
背景层对 Kindle 的访问地址固定为:
- `https://shell.biboer.cn:20001/kindlebg.png`
### 5.2 静态表盘 patch 刷新
静态表盘 patch 不参加分钟级调度。
建议刷新方式:
- 随部署同步一次
- Figma 设计或尺寸变化时重新导出并同步
### 5.3 指针层刷新
指针层刷新触发条件:
- 每分钟一次
指针层数据只依赖 Kindle 本地时间:
- 小时
- 分钟
分钟刷新不需要联网。
## 6. 网页渲染模式设计
为了让 `calendar/` 稳定产出背景与表盘素材,建议支持下面 3 种模式。
### 6.1 `full`
用途:
- 本地开发预览
- 对照 Figma 联调
输出内容:
- 背景层
- 表盘
- 指针预览
### 6.2 `background`
用途:
- 生成 `kindlebg.png`
输出内容:
- 只渲染背景层
- 时钟区域保留占位,但不绘制表盘与指针
### 6.3 `clock-face`
用途:
- 生成静态表盘 patch
输出内容:
- 只渲染表盘主体、刻度和中心底座
- 不绘制时针、分针
### 6.4 URL 约定
建议页面支持如下参数:
```text
/?mode=full
/?mode=background
/?mode=clock-face
```
## 7. 产物约定
推荐最终产出这几类文件:
```text
calendar/dist/kindlebg.png
calendar/dist/dashboard-manifest.json
kindle local:
/mnt/us/dashboard/assets/clock-face.png
/mnt/us/dashboard/assets/hour-hand/000.png ... 719.png
/mnt/us/dashboard/assets/minute-hand/00.png ... 59.png
```
`manifest` 建议至少包含这些字段:
```json
{
"background": {
"path": "kindlebg.png",
"url": "https://shell.biboer.cn:20001/kindlebg.png",
"updatedAt": "2026-03-15T10:00:00+08:00",
"refreshIntervalMinutes": 120
},
"clockRegion": {
"x": 313,
"y": 0,
"width": 220,
"height": 220
},
"clockFace": {
"path": "clock-face.png",
"managedOnKindle": true
},
"clockHands": {
"hourPattern": "assets/hour-hand/%03d.png",
"minutePattern": "assets/minute-hand/%02d.png",
"refreshIntervalMinutes": 1,
"networkRequired": false
}
}
```
说明:
- `x/y/width/height` 先按设计稿记录
- 真正接入 Kindle 时,要换算成最终截图分辨率下的实际像素值
## 8. Kindle 侧刷新策略
### 8.1 已确认的前提
`eips` 支持把 PNG/JPG 绘制到指定坐标,参数包含:
- `-g`
- `-x`
- `-y`
- `-f`
MobileRead Wiki 明确写了:
- `eips -g|-b image_path [-w waveform -f -x xpos -y ypos -v]`
- `-x``-y` 以像素为单位
来源:<https://wiki.mobileread.com/wiki/Eips>
因此,对 Kindle Voyage 而言,“在固定时钟区域重画小图”这个前提是成立的。
### 8.2 推荐流程
#### 启动或背景刷新时
1. 通过 HTTPS 拉取:
- `https://shell.biboer.cn:20001/kindlebg.png`
2. 保存为本地背景缓存
3. 使用全屏刷新显示背景图
4. 在时钟区域重画一次:
- `clock-face.png`
- 当前时针
- 当前分针
#### 每分钟刷新时
1. 读取 Kindle 本机时间
2. 计算:
- `minute_index = 00..59`
- `hour_index = ((hour % 12) * 60 + minute) = 000..719`
3. 在固定坐标先画:
- `clock-face.png`
4. 再画:
- `hour-hand/<hour_index>.png`
5. 再画:
- `minute-hand/<minute_index>.png`
6. 默认做局部/普通刷新
7.`10``15` 分钟对时钟区域补一次全刷,清理残影
### 8.3 功耗模型
这套方式的功耗来源拆成两类:
- 背景刷新:
- 每 2 小时联网一次
- 全屏刷新一次
- 时钟刷新:
- 每分钟本地刷一次小区域
- 不联网
相比“每分钟拉一张整屏背景图”,这会明显省电。
## 9. 代码改造建议
### 9.1 `calendar/`
建议改造点:
- 新增 `mode=background`
- 新增 `mode=clock-face`
- 时钟区域从日历/天气/书摘中完全拆开
- 增加一个定时生成任务,每 2 小时把背景图写到:
- `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png`
- 生成 `dashboard-manifest.json`
### 9.2 `dash/`
建议改造点:
- `dash/src/local/fetch-dashboard.sh`
- 改成只拉取 `kindlebg.png`
- `dash/src/dash.sh`
- 从“单一刷新循环”改为“背景刷新 + 本地时钟刷新”双节奏
- 新增例如:
- `dash/src/local/render-clock.sh`
- `dash/src/local/render-clock-face.sh`
- `dash/src/local/clock-index.sh`
推荐新增配置项:
```sh
export BACKGROUND_URL="https://shell.biboer.cn:20001/kindlebg.png"
export BACKGROUND_REFRESH_SCHEDULE="0 */2 * * *"
export CLOCK_REGION_X=313
export CLOCK_REGION_Y=0
export CLOCK_REGION_WIDTH=220
export CLOCK_REGION_HEIGHT=220
export CLOCK_FULL_REFRESH_INTERVAL_MINUTES=15
```
## 10. 实施顺序
建议按下面顺序落地,避免一次改太多导致链路难排查。
### 阶段 1`calendar/` 分层输出
目标:
- `full/background/clock-face` 三种模式跑通
- 每 2 小时把背景图写到 `calendar/dist/kindlebg.png`
输出:
- 浏览器可预览
- `kindlebg.png` 可被 nginx 直接访问
### 阶段 2时钟静态素材准备
目标:
- 产出 `clock-face.png`
- 产出 `hour-hand``minute-hand` 素材库
输出:
- Kindle 本地时钟素材目录结构确定
### 阶段 3Kindle 本地分钟时钟
目标:
- Kindle 每分钟本地重画时钟
- 不联网
输出:
- 背景低频更新 + 时钟本地高频更新的完整链路
## 11. 推荐结论
当前最稳妥的推进顺序是:
1. 先让 `calendar/` 每 2 小时稳定生成:
- `/Users/gavin/kindle-dash/calendar/dist/kindlebg.png`
2. 再让 Kindle 只从:
- `https://shell.biboer.cn:20001/kindlebg.png`
拉背景图
3. 时钟区域完全本地化:
- 本地 `clock-face.png`
- 本地 `hour-hand/*.png`
- 本地 `minute-hand/*.png`
4. 分钟刷新时:
- 先重画表盘
- 再重画时针
- 再重画分针
也就是说,**背景是远端低频资源,时钟是本地高频资源,二者不要混在同一个刷新链路里**。