# 2026-08-12 工作日志 ## 循环训练页遮挡问题分析与 redesign 规划 - 读取 `pages/timer/timer.wxml/wxss/js`,定位循环训练态(circuit mode)布局。 - 问题根因:`.timer-page` 为 `flex column` 且 `min-height: 100vh` 但无滚动,中间圆环 420rpx + 多层呼吸环 488/620rpx + 顶部卡片 + 状态文字 + 目标卡 + 提示气泡的总高度在小屏/大字体下容易超过视口;底部 `.controls` 用 `margin-top: auto` 贴底,结果会被内容顶出可视区域或被安全区截断。 - 规划方案(未改代码): 1. 圆环从 420rpx 缩到 340rpx,呼吸环同步缩小。 2. 顶部卡片内嵌组进度条,组数/阶段/配置摘要合一,减少一层垂直空间。 3. 状态文字与圆环内副标题、目标卡与姿态提示错位排布,避免信息重复。 4. 底部控件固定高度 + `padding-bottom: calc(32rpx + env(safe-area-inset-bottom))`,保证始终在安全区上方可见。 5. 整体改为 `justify-content: space-between` 的 flex 布局,顶部-中部-底部三大块按可用空间分配。 - 已输出 SVG 线框图,待确认后再落地代码。 ## 方案修正(用户反馈:timer 页是公用页面,仅循环模式有组进度条) - 用户指出原规划把页面当"恒有顶部卡片"设计,切换模式重心会跳。 - 修订方案(未改代码):**组进度从独立顶部面板改为「圆环外圈分段弧」**—— - 在 `progress-ring` canvas 里加 sets/currentSet/isResting 属性,track 环外侧画一圈分段弧(已完成=主色、进行中=主色+端点光点、未开始=浅灰、休息时下一段=success 绿)。 - 单组/自由模式不传 sets → 不渲染分段弧,页面结构与现状完全一致,零影响。 - 圆环内副标题循环模式显示「第 X/Y 组」,单组显示「目标 X」。 - 组数 >15 时退化为圆环下方细线进度条。 - 顶部 circuit-bar 面板(wxml 20-53 行、wxss 15-225 行)整个删除,垂直空间全部还给圆环区与底部控件,从根上解决遮挡。 - 已确认 `components/progress-ring` 为 canvas 2d 实现(_draw() 内 ctx.arc/stroke),扩分段弧同套 API,可行。 ## 落地:循环训练组进度改造(用户批准后实施) - **`components/progress-ring/progress-ring.js`**: - 新增属性 `sets/currentSet/resting/successColor`(sets<=0 时零渲染,单组/自由模式不受影响)。 - 新增 `_drawSetArcs(pulse)`:track 环外侧画分段弧(arcR = radius + ringWidth/2 + 3rpx,弧宽 8rpx,段间隔 2.5°)。三态:已完成=primaryColor、进行中=primaryColor+端点光点、休息时下一段=successColor('#00B578'),未开始=trackColor。 - 呼吸动画用 `canvas.requestAnimationFrame` 循环(周期 1.6s,sin 相位),只清外圈环带(clip 环形避免碰内圈倒计时弧);paused 时冻结为静态;`detached` 时 cancel。 - `_draw()` 末尾补调 `_drawSetArcs(_setPulsePhase || 0.6)`,保证每秒全量重绘后分段弧仍在。 - **`pages/timer/timer.wxml`**:删除整个 `circuit-bar` 面板(原 18-53 行);`` 新增 `sets="{{totalSets}}" currentSet="{{currentSet}}" resting="{{isResting}}"`。 - **`pages/timer/timer.js`**:删除 `circuitDots/circuitShowDots/circuitTotalWork/dotsDone/dotsActive/dotsNext` 字段与 DOTS_MAX 点阵构建;onTick 由 dots* 改为 `ringSubText: 第 X/Y 组`;_initCircuit 的 ringSubText 改为 `共 X 组 · 休息 Y 秒`(rest=0 省略休息段),goalLine 保持 `每组 X · 共 Y 组`。 - **`pages/timer/timer.wxss`**:删除全部 circuit 样式(.circuit-bar/.circuit-dots/.circuit-track/.circuit-meta/.circuit-count/.circuit-phase/.circuit-sub 等)。 - **落地时的设计决策**:组数上限 50(_int clamp),环形 360° 空间充裕,50 段也不挤线(旧 DOTS_MAX=15 是横向卡片宽度受限),故**不做数量降级**;原方案"组数>15 退化线性条"在环形下无必要。 - 验证:node --check 通过;grep 无残留代码引用(仅注释说明文字)。 ## 呼吸环淡化(用户反馈"圆线条太明显") - `timer.wxss` 的 `.breath-aura`:border 6rpx/0.55 → 4rpx/0.20;box-shadow 外层 40rpx/0.45 → 30rpx/0.22、内层 28rpx/0.22 → 20rpx/0.10;auraBreathe 透明度 0.55↔0.85 → 0.30↔0.50,fast 版 0.6↔0.9 → 0.34↔0.55。 - `.breathe-ring` 三档径向渐变强度:0.20/0.26/0.34 → 0.13/0.17/0.22,柔光更淡雅,不与倒计时环抢视觉。 ## 目标卡与提示气泡轻量化(用户反馈"两个提示框不好看") - 问题:`.goal-card` 是白底卡片+右箭头,看起来像可点按钮但无 tap;`.timer-tip` 是白底气泡+左侧绿条+上方小三角+呼吸阴影,与目标卡叠在一起视觉噪音大。 - `timer.wxml`:删除 `.goal-card` 中的右箭头 `goal-arrow`。 - `timer.wxss`: - `.goal-card` 改为轻量胶囊标签:`inline-flex`、主色 8% 淡底、无边框无阴影、圆角 999rpx;padding 24→14rpx/24rpx;图标 40→32rpx;文字 28→26rpx、权重 600→500、主色。 - `.timer-tip` 改为药丸标签:去掉 `border-left`、小三角、box-shadow、tipPulse 呼吸动画;背景主色 8% 淡底(done 态 success 10%);padding 20/32→12/24rpx;圆角 999rpx;图标 40→30rpx;文字 30→26rpx、权重 600→500。 - 验证:grep 无 `goal-arrow/tipPulse/timer-tip::before/border-left: 8rpx` 残留。 ## 目标卡 + 提示气泡合并为单气泡(用户反馈"字体小了、能否合并、页面三模式公用") - 新结构 `.info-bubble`(timer.wxml/wxss):一张主色 8% 淡底圆角卡,两行—— - 上 `info-line--goal`:target 图标 + goalLine,26rpx/500/主色(静态摘要,常驻) - 分隔线 `info-divider`(1rpx 主色 14% 淡线,仅提示出现时渲染) - 下 `info-line--tip`:tipIcon + runningTip,30rpx/600/`--text`(动态提示,比目标行大一号突出);`--done` 态 success 绿 - 三模式共用同一容器:单组/自由/循环都走 info-bubble;循环休息/暂停时 runningTip='' → 分隔线与提示行整体消失,气泡自动收缩只留目标行,布局不跳。 - 旧 `.goal-card/.goal-icon/.goal-text/.timer-tip/.timer-tip-icon/.timer-tip-text` 全部删除,grep 无残留。 ## 状态文案并入信息气泡(用户反馈"撑住"等硬编码文案能否整合进气泡) - 背景:环下方 `.timer-status`(statusText:准备开始/撑住/休息中/已暂停)是独立一行,用户希望圆环下只留一张卡。 - `timer.wxml`:删除 `.timer-status` 行,气泡改为三行结构——上 `info-line--status`(小圆点 + statusText)+ 恒显分隔线 + 中 `info-line--goal` + 提示行(条件渲染)。 - `timer.wxss`:`.timer-status` 样式删除,新增 `.info-line--status/.info-status-dot/.info-status-text`: - 状态行 32rpx/700,小圆点 14rpx 用 `background: currentColor` 跟随状态色(纯 CSS,规避 SVG 图标烤死问题)。 - 配色:`status--running`=主色(圆点呼吸 1.6s);休息时 `status` 字段仍是 running,靠 `isResting` 追加 `info-line--rest` 覆盖为 success 绿(呼吸 1.8s);`status--paused/idle`=text-secondary 灰,paused 圆点冻结动画。 - `.info-bubble` margin-top 24→40rpx(承接原 timer-status 间距)、padding 20→24rpx。 - 关键机制备忘:**resting 时 status 字段值仍是 'running'**,状态行转绿必须靠 `isResting` 单独加类,不能只看 status。 - 验证:grep 无 `timer-status` 代码残留(仅注释文字),js 未改动。 ## 进度弧"0 点对齐"问题:两轮修复被否定,已全部回滚(待用户澄清) - 用户反馈(两次截图):训练时粗进度弧起点与 0 点/顶部起点差一点没对齐,运动过程中更明显(循环与单组模式都出现过)。 - 尝试过两轮修复(progress-ring.js `_drawSetArcs` 半径/清除区微调),用户判定"完全理解错了",**要求撤销**。 - 已回滚:`git diff` 确认 progress-ring.js 与 HEAD(`05c4763` 分段弧初始实现:glowPad=6、arcR=radius+lw/2+3、arcW=8、清除区 clip 内缘 arcR-band)完全一致,`node --check` 通过。 - 教训:对齐类问题不要凭数值推理直接改半径,先让用户明确"0 点"的参照物(刻度环/呼吸环/分段弧?)与出现模式,再定位。 - **最终定位与修复(用户澄清"所有运动中,进度弧零点与表盘 0 点没对齐")**: - 根因确凿:`_drawSetArcs` 每段起点 `+gap/2`(1.25°),**外圈分段弧第一段从 12 点偏右 1.25° 开始**(半径 ~207dpr 处 ≈ 4.5dpr ≈ 1.5px),而内圈进度弧/12 点长刻度都从精确 -π/2 开始 → 循环模式外圈与内圈 0 点肉眼错开。 - 修复:`a0 = startAngle + (i-1)*segAngle`(第一段精确 -π/2 起),`a1 = a0 + segAngle - gap`(段尾留 gap)。段间空隙均匀(2.5°×3),收尾空隙落在 12 点左侧,环带以 12 点为起点。node 验算:段起点 270°/30°/150°,空隙 2.5° 均匀,首段 12 点 ✓。 - 单组/自由模式无分段弧,canvas 内刻度/进度弧同基准(-π/2),数学对齐;若用户仍反馈错位需再查(可能涉及 canvas 布局/参照物)。 - 注意:`gap/2` 对称偏移是"段内居中空隙"的常见写法,环形进度环带与刻度基准对齐时必须从 -π/2 起算。 - **用户叫停("算了,这个问题先不修复了")**:零点对齐问题整体搁置。该轮 progress-ring.js 的角度修复(仅影响循环模式、覆盖不了单组模式现象)已 `git checkout` 回退,与 HEAD 一致。等用户想继续时再排查,方向可能是 canvas 布局/参照物而非半径。 ## 新功能:训练时长低于阈值不记录(config.minRecordSeconds,默认 20 秒) - 需求:训练有效时长 < 20 秒(可配置)不写入记录,并给用户明确提示;阈值收进 config.js。 - **`config.js`**:新增 `minRecordSeconds: 20`(带注释:不写记录、不更新连胜;循环按有效撑持秒数=各组时长之和判断)。 - **`pages/timer/timer.js`**: - `finishTraining()`:`elapsed >= minSec` 才播完成语音(低于阈值不庆祝)。 - `_onCircuitComplete()`:循环自然完成路径同样按 `workTotal >= minSec` 播语音。 - `_saveAndShowCompletion(elapsed)` 开头加**阈值兜底**:`elapsed < minSec` → `_showNotRecorded(elapsed, minSec)` 直接 return。手动结束与循环自然完成两条路径都汇聚到这里,任何入口都不会把短时训练落库。 - 新增 `_showNotRecorded(elapsed, minSec)`:复用完成弹窗(避免系统原生 modal 在 WebView stacking context 下 hit-test 不稳),不保存记录/连胜、无 confetti/streak/科学提示,大数字照常 countUp 到 elapsed;置 `completionNotRecorded: true`、`minRecordSeconds: minSec`、标题"未达最低记录时长"。 - data 新增初始字段 `completionNotRecorded: false`、`minRecordSeconds: 20`。 - **状态残留坑(已修)**:`onTrainAgain`(再来一次)与 `_saveAndShowCompletion` 正常分支都必须显式 `completionNotRecorded: false`,否则上轮未记录后重练,下次正常完成弹窗会误显示"未计入记录"且隐藏分享/记录按钮。 - **`timer.wxml`**:完成弹窗新增 `.completion-not-recorded` 提示块(infoFill 图标 + "本次训练未计入记录" + 副行"撑满 X 秒以上才会记录并计入连续打卡");未记录态 `wx:if="{{!completionNotRecorded}}"` 隐藏"查看记录/分享"(没有成绩可看/可晒),仅留"再来一次 + 回到首页"。 - **`timer.wxss`**:新增 `.completion-not-recorded` 主色淡底信息块(与 completion-tip 同风格,tipIn 延迟 0.5s 入场)。 - 保留逻辑:`elapsed < 3` 秒仍走 toast「训练时间太短」+ navigateBack(几乎未开始,不弹完成弹窗)。 - 验证:node --check 通过;grep 确认 minRecordSeconds/completionNotRecorded/_showNotRecorded 三处字段在 config/js/wxml 引用一致。 ## 分段弧两端粗细不一致(已修) - 用户反馈:循环训练时分段弧"两端粗细不一致"。 - 根因(progress-ring.js `_drawSetArcs`):发光光点坐标固定取段尾角 `a1`(`dx=cx+arcR*cos(a1)`),当前段(active)/休息预告段(next)的尾端 = 光点+8dpr 发光晕(直径≈弧宽+),首端却只是 `lineCap:'round'` 普通圆头 → 同段弧尾粗头细。已完成/未开始段两端都是普通圆头,粗细一致,所以现象集中在运动中那一段。 - 修复:光点移到**段中点** `am=(a0+a1)/2`(3 行),两端自然对称,光点仍作"当前组"标记。node --check 通过。 - 注意:若以后想给分段弧加"当前组跟随倒计时移动"的进度感,光点可改为随剩余时间在段内插值,但需先确认用户是否要这个效果。 ## 分段弧间距 + 光点置顶(用户反馈"两弧太近、弧压着光点") - 用户反馈两点:①分段弧与进度弧距离太近;②光点被分段弧压住(视觉上弧盖着光点)。 - **间距修复**(progress-ring.js,size=420 时验证): - 根因:间距 = gap − arcW/2 = 3−4 = **−1dpr(重叠 1)**。因为分段弧 arcR 仅比进度弧外缘靠外 3dpr,而弧半宽 4dpr。 - `_draw()` 的 `glowPad` 6→14dpr(进度弧半径内缩 8,给外圈腾空间);`_drawSetArcs()` 的 radius pad 6→14(**两处必须同步**,已加注释)且 arcR 间距 `+3→+9dpr`。结果:进度弧外缘 204→196、分段弧内缘 203→201,间距 −1→**+5dpr**。副作用:单组/自由模式的进度环也内缩 ~8dpr(约 4%),中心数字区略小;进度弧尾端发光点 shadowBlur=12 阴影反而从"被 canvas 裁"变完整。 - 段间空隙 gap=2.5° 是段间距,**与环间距无关,不动**。 - **光点置顶**:原实现光点在逐段循环内、每段弧 stroke 之后画,理论上应在弧上,但视觉上被 round cap 相邻段边缘"吞掉"。重构为**两遍绘制**:pass1 只画所有弧并收集 emphasized 段光点(x/y/color),pass2 统一在所有弧之上画光点。同时光点加大加亮:dotR 从 `0.85~1.2×弧半宽` → `1.15~1.4×`,alpha `0.55~1.0` → `0.7~1.0`,白芯 0.85→0.9,读起来像"珠珠骑在弧上"。 - 光点仍居段中点(沿用上轮两端对称修复);外缘 209dpr 不超 canvas 半宽 210,与 breath-aura(内缘 ~232)无重叠。 - 验证:node --check 通过;数值全部核算。未提交 git。 - **间距仍不够(用户反馈"还是太近")→ 再翻倍**:canvas 半宽 210 是硬上限,剩余空间仅 ~5dpr,要明显拉开只能继续内缩进度弧。glowPad 14→18、arcR 外移量 9→14,间距 **5→10dpr**(进度弧外缘 196→192,分段弧内缘 201→202,光点外缘 211.6 超画布 0.8px 亚像素无感)。进度环直径累计缩 ~6%(408→384)。约束备忘:**间距 = arcR外移量 − arcW/2,且 ≤ glowPad − arcW − margin**;想再拉开只能继续缩环或减弧宽。