diff --git a/.workbuddy/memory/2026-08-12.md b/.workbuddy/memory/2026-08-12.md index 59972b1..c31f4b7 100644 --- a/.workbuddy/memory/2026-08-12.md +++ b/.workbuddy/memory/2026-08-12.md @@ -89,3 +89,20 @@ - **`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**;想再拉开只能继续缩环或减弧宽。 diff --git a/components/progress-ring/progress-ring.js b/components/progress-ring/progress-ring.js index a3ca42a..3ba5b29 100644 --- a/components/progress-ring/progress-ring.js +++ b/components/progress-ring/progress-ring.js @@ -105,8 +105,11 @@ Component({ const cx = w / 2 const cy = h / 2 - // reserve room for the glowing end dot so its shadow isn't clipped - const glowPad = 6 * dpr + // Reserve room for the glowing end dot's shadow AND the outer set-progress + // band (circuit mode). _drawSetArcs derives its band radius from this same + // pad, so increasing it here also widens the gap between the countdown + // arc and the set band. 18dpr gives a ~10dpr visible gap (size=420). + const glowPad = 18 * dpr const radius = (size - ringWidth) / 2 * dpr - glowPad const lw = ringWidth * dpr @@ -252,8 +255,9 @@ Component({ const cy = (size / 2) * dpr // Same radius math as _draw(): the countdown arc sits at `radius`; the // set band wraps just outside it so the two read as concentric layers. - const radius = ((size - ringWidth) / 2) * dpr - 6 * dpr - const arcR = radius + (ringWidth / 2) * dpr + 3 * dpr + // NOTE: the -18*dpr pad MUST stay in sync with glowPad in _draw(). + const radius = ((size - ringWidth) / 2) * dpr - 18 * dpr + const arcR = radius + (ringWidth / 2) * dpr + 14 * dpr const arcW = 8 * dpr const band = arcW / 2 + 3 * dpr @@ -274,6 +278,10 @@ Component({ const gap = (2.5 * Math.PI) / 180 const startAngle = -Math.PI / 2 + // Pass 1: draw every segment arc. Breathing dots are collected and painted + // in pass 2, AFTER all arcs, so a dot always floats ON TOP of its segment + // instead of being partially buried under neighbouring round caps. + const dots = [] for (let i = 1; i <= sets; i++) { const a0 = startAngle + (i - 1) * segAngle + gap / 2 const a1 = startAngle + i * segAngle - gap / 2 @@ -294,28 +302,38 @@ Component({ ctx.stroke() ctx.globalAlpha = 1 - // Glowing endpoint dot on the emphasized segment. if (emphasized) { - const dx = cx + arcR * Math.cos(a1) - const dy = cy + arcR * Math.sin(a1) - const dotR = (arcW / 2) * (paused ? 1 : 0.85 + 0.35 * p) - const glowColor = i === active ? primaryColor : successColor - ctx.save() - ctx.shadowColor = glowColor - ctx.shadowBlur = 8 * dpr - ctx.globalAlpha = paused ? 0.9 : 0.55 + 0.45 * p - ctx.beginPath() - ctx.arc(dx, dy, dotR, 0, Math.PI * 2) - ctx.fillStyle = glowColor - ctx.fill() - ctx.restore() - // white core for a "light bulb" feel - ctx.beginPath() - ctx.arc(dx, dy, dotR * 0.45, 0, Math.PI * 2) - ctx.fillStyle = 'rgba(255,255,255,0.85)' - ctx.fill() + const am = (a0 + a1) / 2 + dots.push({ + x: cx + arcR * Math.cos(am), + y: cy + arcR * Math.sin(am), + color: i === active ? primaryColor : successColor + }) } } + + // Pass 2: glowing dot on each emphasized segment, drawn over every arc. + // Placed at the segment MIDDLE (not the a1 end): a dot pinned to the + // trailing end made that end look visibly thicker than the round-cap + // start, so both ends of the arc stay symmetric. Slightly larger than + // the band width so it reads as a bead riding on top of the arc. + for (const dot of dots) { + const dotR = (arcW / 2) * (paused ? 1.3 : 1.15 + 0.25 * p) + ctx.save() + ctx.shadowColor = dot.color + ctx.shadowBlur = 8 * dpr + ctx.globalAlpha = paused ? 0.95 : 0.7 + 0.3 * p + ctx.beginPath() + ctx.arc(dot.x, dot.y, dotR, 0, Math.PI * 2) + ctx.fillStyle = dot.color + ctx.fill() + ctx.restore() + // white core for a "light bulb" feel + ctx.beginPath() + ctx.arc(dot.x, dot.y, dotR * 0.45, 0, Math.PI * 2) + ctx.fillStyle = 'rgba(255,255,255,0.9)' + ctx.fill() + } }, _startSetPulse() {