机型
新增 38 款圆屏 + CIQ ≥ 4.2 的机型:fenix 7 Pro 全系、Venu 2/3S、
Approach S50/S70、Descent G2/Mk3、D2 系列、Instinct 3 AMOLED、
FR255S/265S/570/955 等。其中 34 款的屏宽已有现成图标资源。
机型列表和 monkey.jungle 改由 tools/gen_devices.py 扫描本机 SDK 生成,
筛选条件是「支持 watchFace + 圆屏 + CIQ ≥ 4.2」(Complications 要 4.2)。
55 条资源路径不再手写。
新增 218px 与 360px 两档图标。218px 上图标只有 14×14,设计稿里太阳与云
之间那道 1.7 设计像素的暗缝连一个物理像素都占不到,光栅化出来是一坨没有
结构的橙块 —— 该尺寸改用按目标像素直接摆的简化标记。
常亮模式:结论是不改
为了压所谓的 10% 点亮率,一度把进度环也砍掉了。换成平均亮度(AMOLED 功耗
的合理代理)重新量之后发现:普通模式 14.1%,常亮 2.9%,本来就只有 1/5;
砍掉环只从 3.01% 降到 2.91%。0.1 个百分点换掉一个可见特征不划算,环留着。
两种度量的差异和这个决定都写进了代码注释和 README。
其它
- LICENSE(MIT)。只覆盖代码 —— design/ 下的设计稿版权归第三方,
发布前需取得原作者授权或替换视觉,文件内已注明。
- UserProfile.getProfile() 加异常保护(这个 API 家族已经坑过三次)
- CHANGELOG.md,版本号 1.0.0 → 1.1.0
验证:55 款全部编译通过(分三批 18+18+19);.iq 上架包可正常导出。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
24 KiB
Fenix 8 V3 — Garmin 数据表盘
按 watchface-kit 设计稿 1:1 还原的多指标数据表盘。设计画布 500×500,渲染时按屏宽等比缩放 s = screenWidth / 500,支持圆形 AMOLED 与圆屏 MIP 共 55 款设备(Fenix 7/8 全系含 Pro、Epix 2、Enduro 3、Venu 2/3、FR 系列、Approach S50/S70、Descent、D2、Instinct 3 AMOLED 等)。
- SDK:Connect IQ SDK 9.1.0(
monkeyc9.1.0,minSdkVersion="4.2.0") - 语言:Monkey C;界面文案含英文与简体中文
- 类型:
watchface - 应用 ID:
f8a3c1b2c3d44e5f8a9b0c1d2e3f4a5b
设计稿本体(7 张 SVG + tokens + 逆向脚本)在 design/,是本仓库所有坐标与配色的唯一来源。
1. 目录结构
fenix8v3-watchface/
├── manifest.xml # 应用清单(ID / 支持设备 / 语言)
├── monkey.jungle # 构建配置
├── design/ # ⭐ 设计稿原件(唯一事实来源)
│ ├── watchface-0N-*.svg # 7 套主题的矢量还原稿
│ ├── watchface-tokens.json # 配色 token
│ └── rebuild.py # 原作者从位图逆向出 SVG 的脚本(仅存档,不参与构建)
├── tools/
│ ├── gen_themes.py # design/*.svg -> source/Themes.mc
│ ├── gen_fields.py # 字段表 -> FieldTable.mc + settings.xml + 两种语言文案
│ ├── gen_devices.py # 扫描 SDK -> manifest 机型列表 + monkey.jungle
│ ├── gen_icons.py # design/*.svg -> 各尺寸图标位图 + 启动图标
│ └── gen_preview_svg.py # source/Themes.mc -> preview-themes.svg + preview.html
├── source/
│ ├── Fenix8V3App.mc # 应用入口(12 行壳子)
│ ├── Fenix8V3View.mc # 绘制顺序:进度环 / 各元素 / 常亮模式
│ ├── Layout.mc # 500 画布常量、缩放、字号选择、文字绘制
│ ├── Icons.mc # 图标:位图三件套 + 其余图元
│ ├── Fields.mc # 125 项数据字段的取值与格式化
│ ├── Solar.mc # 日出/晨昏蒙影/黄金蓝调时刻的太阳高度角计算
│ ├── Owm.mc # OpenWeatherMap 缓存读取
│ ├── Comp.mc # 系统 / 第三方 Complication 桥接
│ ├── Fenix8V3Background.mc # 后台服务:拉取 OWM
│ ├── Settings.mc # 所有设置项的读取与默认值
│ ├── FieldTable.mc # ⚠️ 自动生成:字段 id 表
│ └── Themes.mc # ⚠️ 自动生成:七套主题配色
├── resources/ # 英文文案 + 设置项定义(部分自动生成)
├── resources-chn/ # 简体中文文案
├── resources-icons-<宽度>/ # ⚠️ 自动生成:按屏幕尺寸光栅化的图标
├── resources-launcher-<尺寸>/ # ⚠️ 自动生成:各尺寸启动图标
└── bin/ # 编译产物(已 gitignore)
重新生成全部派生文件:
python3 tools/gen_devices.py \
&& python3 tools/gen_themes.py && python3 tools/gen_fields.py \
&& python3 tools/gen_icons.py && python3 tools/gen_preview_svg.py
gen_devices.py 要先跑 —— 它决定了需要哪些图标尺寸,gen_icons.py 的
WIDTHS / LAUNCHER 两张表必须跟它对齐,少一档就会有机型编译不过。
三份渲染实现(表盘 / SVG 预览 / HTML 预览)只有 一份颜色数据:Themes.mc。预览脚本直接解析它,不会与固件漂移。字段表、设置项、两种语言的文案同样只有 tools/gen_fields.py 里那一张表。
1.5 代码结构
一句话:位置归 Layout,内容归 Fields,配色归 Themes,设置归 Settings,Fenix8V3View 只剩画的顺序。
| 模块 | 负责 | 不负责 |
|---|---|---|
Layout |
500 基准画布的全部坐标常量、px() 缩放、极坐标换算、字号选择与自动缩字、渐变文字的分带绘制 |
具体画什么 |
Icons |
三个位图图标的加载与染色、电池、蓝牙、其余数据项的图元 | 图标该出现在哪个数据位(由字段 id 决定,但查表在这里) |
Fields |
75 项字段的取值、单位换算、本地化文案(星期/月份/标签) | 排版 |
Settings |
每个设置项的读取与默认值,包含空值回退 | 设置项的 UI 定义(在 settings.xml) |
Fenix8V3View |
onUpdate 的绘制顺序、进度环、常亮模式 |
以上任何一项的细节 |
Fields.value() 按分类拆成 7 个函数(dateTime / activity / body / system / environment / weather / custom),每个在不认识 id 时返回 null,value() 依次尝试。id 是交错的(1、2、3 是活动,8、11、12 是身体,14 又是活动),所以按区间派发行不通。
Fields.Ctx 每次绘制构造一个,把 ActivityMonitor / Weather / UserProfile / SensorHistory 的查询懒加载并缓存——四个数据位可能都要天气,但只查一次;没有数据位用到 UserProfile 时就完全不查。
2. 布局(500 基准,取自设计稿 SVG 路径的墨迹包围盒)
| 元素 | 位置 | 说明 |
|---|---|---|
| 进度环 | 内半径 231.09,外半径 249.10 |
见 §3 |
| 电量图标 | x=192.70,cy=53.20,26.20×17.81 |
圆角外框 + 按电量填充 + 右侧触点 |
| 电量数值 | 左对齐 x=232.00 |
NN% |
| 上方双数据 | 图标 cy=90,数值 cy=131.06;左 x=157.00,右 x=343.70 |
居中对齐 |
| 时间 | cy=221.10;小时右对齐 x=211.40,分钟左对齐 x=276.02 |
竖向渐变,见 §4 |
| 冒号 | x=237.88,24.24×21.30,y=183.68 / 234.57 |
两块矩形 |
| 日期带 | y=286.30,高 44.19,文字 cy=308.10 |
蓝牙 cx=36.88、星期 cx=178.05、月日 cx=298.28、AM/PM 右对齐 465.89 |
| 底部数值 | cy=366.76;左格右对齐 234.30,右格左对齐 267.40 |
过宽时自动降一档字号 |
| 底部标签 | cy=410.88,同上对齐 |
跟随所选数据(DST / STEP / 距离 …) |
字号按设计稿的**字面高度(cap height)**选取:日期带与电量 25.34、上方数据与底部行 31.50、时间 98.70。
3. 进度环
刻度是等角宽的环形扇区(fillPolygon 四点),不是径向线段。
- 顶弧:21 根,角度
6.5 + 4.0k(k=0..20),角宽3.108° - 底弧:21 根,角度
6.005 + 3.0k,角宽2.086° - 四象限:右上
+a、左上-a、左下180+a、右下180-a(角度为自 12 点起顺时针) - 锚点圆点:半径
9.00,位于圆心距240.10、角度0 / 93.15 / 180 / 266.85
点亮方向(与设计稿一致):两条顶弧从 9 点 / 3 点一侧朝 12 点填充;两条底弧从 6 点朝两侧填充。
每根刻度的颜色是设计稿里逐根取样的实测值(Themes.RINGTOP[21] / RINGBOTTOM[21]),不是插值近似。
已知近似:设计稿中底弧最多只点亮 14/21 根,因此
RINGBOTTOM的第 15–21 项无法实测,由gen_themes.py对最后 8 根做最小二乘线性外推得到。只有当指标超过约 67% 时才会显示这几根。
指标进度换算
| idx | 指标 | 分母 |
|---|---|---|
| 0 | 步数 | stepGoal |
| 1 | 卡路里 | 固定 2000 kcal |
| 2 | 距离 | 10 km |
| 3 | 楼层 | floorsClimbedGoal(缺省 10) |
| 4 | 动动条 | MOVE_BAR_LEVEL_MAX(=5) |
4. 时间数字的竖向渐变
设计稿的小时 / 分钟数字带竖向多段渐变(如 Ember 分钟由 #FEAA00 渐变到 #FF2600)。Monkey C 无法给文字直接填渐变,实现方式是同一串数字画 11 遍,每遍用 dc.setClip 限制在一条横向色带内,色带颜色取自 Themes.HOURS / Themes.MINUTES(各 11 个停止点,实测自设计稿的 linearGradient)。首尾两条色带向外延伸,保证字形不会有未绘制的缝隙。
5. 字号选择
Connect IQ 只暴露 getFontAscent / getFontDescent / getFontHeight,没有字面高度接口,而 ascent 里含有数字和大写字母用不到的变音符空间。因此代码用两个实测比例把 ascent 折算成 cap height:
文本字体(Roboto 系) cap ≈ 0.76 × ascent
数字字体(Bionic 系) cap ≈ 0.71 × ascent
比例是在模拟器里渲染后、从截图上量出墨迹得到的(fenix847mm:FONT_XTINY ascent 29 → 大写高 22px;FONT_NUMBER_MILD ascent 82 → 数字高 58px)。pickFont 取折算后最接近目标 cap height 的字体,而不是"最大且不超过"——后者会在设计尺寸落在两档字体之间时白白小一号。
各机型内置字体不完全相同(Roboto-Regular / RobotoCondensed-Bold / Yantramanav / 各机型位图字体),比例会有几个百分点误差。设计稿使用的是更粗更方的第三方字体,不随包分发,字形差异无法消除。
6. 七套主题
由 tools/gen_themes.py 从 design/watchface-0N-*.svg 生成。每套包含:顶/底弧各 21 根刻度色、未点亮刻度色、锚点色、日期带底/字色、冒号色、小时/分钟各 11 段渐变、强调色(图标与 DST/STEP 标签)、主文字色(各项数值)。
| # | 主题 | 主色调 | 强调色 | 日期带 |
|---|---|---|---|---|
| 1 | Ember | 黄 → 红 | #FEAA05 |
橙底黑字 |
| 2 | Aurora | 黄绿 → 青 | #ADABAE |
深灰底白字 |
| 3 | Brass | 金铜 | #B57E4B |
深棕底白字 |
| 4 | Voltage | 蓝 → 黄 | #ADABAE |
蓝底黑字 |
| 5 | Reef | 橙 → 青 | #FEAA05 |
青底黑字 |
| 6 | Acid | 黄绿(近单色) | #C6FA03 |
深灰底黑字 |
| 7 | Kelp | 橙 → 绿 | #34C294 |
深灰底黑字 |
改配色:编辑 / 替换 design/ 里的 SVG,然后
python3 tools/gen_themes.py && python3 tools/gen_preview_svg.py
新增主题需同步在 settings.xml 的 Theme 列表与 strings.xml(含 resources-chn/)中登记。
7. 可配置设置与数据字段
19 项设置,在 Garmin Connect 手机 App 的「表盘设置」里改(定义在 resources/settings/settings.xml,默认值在 properties.xml,两者由 tools/gen_fields.py 生成)。
| 设置 | 键 | 默认 |
|---|---|---|
| 主题 | Theme |
1 (Ember) |
| 四段进度环指标 | RingTR/TL/BR/BL |
步数 / 卡路里 / 动动条 / 楼层 |
| 四个数据位 | LeftTop RightTop BottomLeft BottomRight |
日出 / 高低温 / 距离 / 步数 |
| 温度单位 | TempUnit |
跟随手表 |
| 第二时区 1 / 2 偏移(分钟) | AltOffset1 AltOffset2 |
0 |
| 自定义文字 1 / 2 / 3 | Custom1/2/3 |
空 |
| 显示顶行 / 日期带 / 底行 | ShowTop ShowBand ShowBottom |
true |
进度环指标:步数、卡路里、距离、楼层、动动条、活动分钟(对周目标)、身体电量。
数据字段
四个数据位共用一张 156 项 的字段表,编号沿用 watchface.io/docs/datafields,两边可以直接对照。底部两格的小标签跟随所选字段自动变化,不需要单独配置。
| 分类 | 字段 |
|---|---|
| 日期时间 | 秒(前导零两种)、时间、UTC 时间、第二时区 1/2、星期月日、星期日、月日、日、星期(简写/全称)、月份(名称/数字)、上午下午、周数(ISO / 日历) |
| 活动 | 步数、步数取整、剩余步数、卡路里、活动卡路里、活动分钟(总/中强度/高强度/本周)、距离、上楼层数、下楼层数、爬升高度、推行距离、推行次数、本周 / 近 7 天 / 近 28 天累计(步数/卡路里/距离/楼层/活动分钟) |
| 身体 | 心率、静息心率、压力指数、血氧、呼吸频率、身体电量、活动条(正/反向)、体重、BMI、恢复时间、最大摄氧量(跑/骑) |
| 系统 | 电量、剩余天数、电量+天数、闹钟、通知、勿扰、蓝牙、太阳能强度 |
| 环境 | 海拔、海平面气压、环境气压、日出、日落、下一太阳事件、距太阳事件、月龄、月球照明、民用/航海/天文晨昏蒙影(各 2 项)、黄金时刻(晨/昏)、蓝调时刻(晨/昏) |
| 天气 | 气温、最高/最低、体感、最高、最低、湿度、降水概率、风速、风向角、风向、更新时间、海平面气压、紫外线、能见度、露点 |
| OpenWeatherMap | 气温、体感、最高/最低、天气描述(简/详)、湿度、气压、能见度、风速、风向角、风向、阵风、1h 降雨、1h 降雪、云量、降水概率、露点、明日与后日气温及高低温、城市名、更新时间、空气质量(AQI 等级 + 描述 + PM2.5 + PM10 + CO)(共 30 项) |
| 系统 Complication | 下一个日程、训练状态、完赛预测(5k/10k/半马/全马)、配速预测(同上四项)、最近高尔夫得分、本周跑步距离、本周骑行距离、第三方 App 1–5 |
| 自定义 | 自定义文字 1 / 2 / 3、自定义时间格式、日期格式 1 / 2、事件倒计时 |
加字段:在 tools/gen_fields.py 的 FIELDS 表里加一行,跑一遍生成器,再到 source/Fields.mc 的 value() 里补一个分支。设置项、两种语言的文案、表盘标签会自动跟上。
系统 Complication
Toybox.Complications(CIQ 4.2+,17 款设备全都有)让表盘可以订阅系统和其它 App 发布的复杂功能。这条路解锁了一批原本以为做不到的字段:
- 日历事件 —— CIQ 确实没有日历 API,但系统把「下一个日程」作为 complication 发布了出来
- 训练状态、完赛/配速预测、最近高尔夫得分 ——
ActivityMonitor.Info和UserProfile上都没有,这里有 - 本周跑步 / 骑行距离 —— 这是按运动分类的,
ActivityMonitor的按天历史做不出来 - 第三方 App 1–5 —— CGM、补水、行情这类 App 发布的复杂功能,按序号取
需要 ComplicationSubscriber 权限。第三方 App 那五个槽位按「有值且有短标签」过滤后按序号取,避免序号随手表上装了什么 App 而漂移。
自定义格式与倒计时
时间/日期格式串用单字母占位符(设置项能填的长度有限,这样中英文都好写):
| 占位符 | 含义 | 占位符 | 含义 |
|---|---|---|---|
h / H |
小时(跟随 12/24 制)/ 补零 | D |
日 |
m / s |
分钟 / 秒(补零) | M / N |
月(数字 / 名称) |
a |
上午/下午(24 小时制下为空) | Y / y |
四位年 / 两位年 |
w |
星期 |
其它字符原样输出,所以 D/M → 10/9,w N D → 周四 9月 10。
事件倒计时填 YYYY-MM-DD,显示还剩几天。
秒针与后台天气
秒(850/869)通过 onPartialUpdate 每秒重绘:只有当某个数据位真的选了秒时才请求这个回调,并且只在该格的裁剪区内重绘,不动整个表盘。常亮的 AMOLED 机型不参与——那时数据位本来就不显示。
OpenWeatherMap 需要用户在设置里填自己的 API key(免费档即可)。没填就完全不联网,25 个 OWM 字段都显示 --。填了之后由后台服务每 30 分钟拉一次(当前天气 + cnt=16 的短预报,够算出明日/后日高低温),结果压平成一个小字典存进 Application.Storage,表盘只读不联网。
位置来自天气观测点或最近一次 GPS 定位,并缓存在 Storage 里——两个来源都会间歇性为空,缓存让日出和晨昏蒙影不会一会儿有一会儿没有。
⚠️ 仍然没有实现的,以及原因
| 没做的 | 原因 |
|---|---|
| 按运动细分的多数汇总(游泳、自定义运动 1/2 的距离/时长/次数,约 60 项) | ActivityMonitor.getHistory() 只有按天总计,没有按运动分类的历史。跑步和骑行的本周距离例外——系统把这两项作为 complication 发布了,已经做了;游泳和自定义运动没有对应的 complication 类型 |
| 日历事件、计时器 | 日历已通过 Complication 做了。计时器仍然做不了:表盘拿不到触摸输入,没法启停 |
| StormGlass.io 潮汐(5 项) | 需要额外的付费 API key,且与本表盘的定位无关 |
| 第三方 App 复杂功能(CGM、Hydration、QuoteGlance 等 12 项) | 需要用户装了对应的 App,并通过 Complications API 订阅 |
| 睡眠得分 | ActivityMonitor.Info 上没有,也没有对应的 complication 类型(SDK 9.1.0 实测)。训练状态与完赛/配速预测已改由 Complication 提供 |
| 高尔夫成绩 | 无对应 API |
| 标记类字段(Marker,约 35 项) | 那是给刻度型表盘做环形标记用的,本表盘的进度环用的是自己的指标体系 |
| OWM 的 UVI | 要 One Call 3.0 订阅。AQI 已经做了——空气质量走 /data/2.5/air_pollution,那个接口在免费档就开放。露点用 Magnus 公式从温湿度算出 |
8. 数据来源与权限
| 元素 | API |
|---|---|
| 步数 / 卡路里 / 距离 / 楼层 / 动动条 / 活动分钟 / 呼吸 / 压力 / 恢复时间 | ActivityMonitor.getInfo() |
| 心率 | Activity.getActivityInfo().currentHeartRate,回退 ActivityMonitor.getHeartRateHistory() |
| 血氧 / 身体电量 | SensorHistory(需 SensorHistory 权限) |
| 静息心率 / 体重 / 身高 / 最大摄氧量 | UserProfile.getProfile()(需 UserProfile 权限) |
| 海拔 / 环境气压 / 海平面气压 | Activity.getActivityInfo() |
| 日出 / 日落 | Weather.getSunrise/getSunset(location, time),位置取 CurrentConditions.observationLocationPosition,回退 Activity.currentLocation |
| 气温 / 体感 / 湿度 / 降水 / 风 | Weather.getCurrentConditions() |
| 最高 / 最低气温 | Weather.getDailyForecast()[0] |
| 电量 / 剩余天数 / 太阳能 | System.getSystemStats()(battery 是 0–100 的百分比,不是 0–1) |
| 闹钟 / 通知 / 勿扰 / 蓝牙 / 单位制 | System.getDeviceSettings() |
| 月龄 / 月球照明 | 自算(儒略日 + 朔望月 29.530588853 天) |
| 周数 | 自算(ISO 8601 与日历周两种) |
权限
manifest.xml 声明五项:UserProfile(静息心率、体重、VO2max)、SensorHistory(血氧、身体电量)、Communications 与 Background(OpenWeatherMap)、ComplicationSubscriber(日历、训练状态、第三方 App)。用户安装时会看到授权提示。
Sensor权限不能用于type="watchface"(除非同时申请Background),编译器会直接拒绝。所以海拔与气压走Activity.Info而不是Sensor.getInfo()。
单位与制式跟随手表:is24Hour、distanceUnits、temperatureUnits(温度可用 TempUnit 覆盖)。天气与日出需手表已配对并同步过数据,否则显示 --。
8.2 常亮显示(AOD)
AMOLED 机型在常亮模式下必须压低点亮像素,否则既费电又有烧屏风险,Garmin 上架审核也会卡。onEnterSleep / onExitSleep 切换到 drawAmbient():
- 不画日期带底色(整条亮橙色横幅是最费电的元素)
- 进度环只画点亮的刻度,且整体压暗到 45%
- 时间与日期压暗到 55%,其余数据位全部隐藏
- 整个画面按分钟在 ±4px 内游走,避免固定像素长期点亮
只在 DeviceSettings.requiresBurnInProtection 为真时启用;MIP 机型不受影响,照常全量绘制。
实测(fenix847mm,取表盘内区):
| 点亮像素数 | 平均亮度(功耗代理) | |
|---|---|---|
| 普通模式 | 27.6% | 14.1% |
| 常亮模式 | 10.4% | 2.9% |
两个口径差别很大:「点亮像素数」把每个抗锯齿边缘像素都算成亮的,和实际功耗对不上。曾试过连进度环也砍掉,平均亮度只从 3.01% 降到 2.91% —— 0.1 个百分点换掉一个可见特征不划算,所以环保留。
模拟器可用 Settings → Display Mode 切换验证。
8.5 多语言
manifest.xml 声明了 eng 与 chn,两套文案分别在 resources/strings/ 与 resources-chn/strings/,由 monkey.jungle 的 base.resourcePath = resources;resources-chn 同时纳入编译(目录名的 -chn 后缀就是语言标记)。手表会按系统语言自动选择。
表盘上的文字也走资源,不是硬编码:
| 资源 | 英文 | 中文 |
|---|---|---|
DowNames |
SUN,MON,…,SAT |
周日,周一,…,周六 |
MonNames |
JAN,FEB,…,DEC |
1月,2月,…,12月 |
DateFormat |
$1$ $2$ → SEP 9 |
$1$$2$日 → 9月9日 |
Meridiem |
AM,PM |
上午,下午 |
FieldLabels |
TEMP,KCAL,STEP,DST,… |
气温,卡路里,步数,距离,… |
逗号分隔的列表在 loadStrings() 里一次性读入并拆分(loadResource 每次调用开销不低,不宜放进 onUpdate)。
中文字形依赖手表固件里的 APAC 字体包(
fontSet: apac_twcn/apac_chn)。国行机型自带;国际版若未安装中文字体包,会退回英文或显示方块。模拟器可用Settings → Language → Chinese (Simplified)验证。
9. 构建
需安装 Connect IQ SDK 9.1.0,monkeyc 在 PATH 中,并备好 developer_key.der(不在仓库内,需自行生成或提供)。
monkeyc -o bin/Fenix8V3.prg -f monkey.jungle -y developer_key.der -d fenix847mm
全部 17 款设备批量编译(已验证通过):
for d in enduro3 fenix843mm fenix847mm fenix8pro47mm fenix8solar47mm fenix8solar51mm \
epix2 epix2pro47mm epix2pro51mm fenix7 fenix7x fenix7s fr965 fr265 fr255 \
venu3 vivoactive5; do
monkeyc -o "bin/Fenix8V3_${d}.prg" -f monkey.jungle -y developer_key.der -d "$d" \
&& echo "[$d] OK" || echo "[$d] FAIL"
done
屏幕分辨率跨度较大(FR255S 218×218 至 454×454 共 8 档),全部按 s = width/500 缩放。
机型列表和 monkey.jungle 由 tools/gen_devices.py 扫描本机已安装的 SDK 设备生成:筛选条件是「支持 watchFace + 圆屏 + CIQ ≥ 4.2」(Complications 需要 4.2)。注意结果取决于本机装了哪些设备,跑之前先确认 SDK 设备装全了,脚本会打印找到的数量。
10. 模拟器与侧载
connectiq & # 启动模拟器
monkeydo bin/Fenix8V3_fenix847mm.prg fenix847mm # 载入表盘
非 release 编译会同时产出 bin/*-settings.json,可在模拟器 View → Settings 载入以实时切换主题与指标。注意模拟器会持久化已保存的设置,改 properties.xml 的默认值对已运行过的实例不生效,需先重置应用数据。
真机侧载:用 Garmin Express 或 Connect 手机 App 导入对应机型的 bin/Fenix8V3_<device>.prg,或把 .prg 拷入手表 /GARMIN/Apps/。
11. 预览
python3 tools/gen_preview_svg.py
产出 preview-themes.svg(七套主题一览)与 preview.html(可切换主题的单页)。两者用网页字体渲染文字,几何与配色可信,字形不可信 —— 真机字形以模拟器截图为准。
12. 与设计稿的差异
- 字体。设计稿用的是一款更粗、更方的第三方字体;Garmin 只提供内置 Roboto / Bionic 系列,且各机型不同。字号按 cap height 对齐,字形无法一致。
- 图标。设计稿里的图标是位图描出的密集矢量路径(数百个点),不适合逐帧绘制;代码用圆、多边形、线段按实测的子形状位置重建了电量、日出/日落、多云三个图标。其余数据项(卡路里 / 步数 / 距离 / 楼层)设计稿未提供图标,为自绘。
- 底弧高段颜色为外推值,见 §3。
- 进度环是实时数据,设计稿中的是固定演示状态(右上 21/21、左上 10/21、右下 9/21、左下 14/21)。
13. 设计稿版权
design/ 内的 SVG、tokens 与 rebuild.py 来自 watchface-kit,版权归原作者所有。本仓库是按该设计稿实现的 Monkey C 表盘。