feat(timer): 循环训练组进度改为圆环分段弧 + 信息气泡三行合一

- progress-ring 新增 sets/currentSet/resting/successColor 属性,
  圆环外圈绘制分段弧(已完成=主色/当前=呼吸光点/休息下一组=绿),单组模式零渲染
- timer 删除顶部 circuit-bar 进度面板,布局骨架三模式统一
- 呼吸环淡化(描边 0.55→0.20,柔光渐变强度减半)
- 目标卡与提示气泡合并为 info-bubble,状态文案(撑住/休息中/已暂停)并入气泡顶部
This commit is contained in:
2026-08-12 09:39:41 +08:00
parent 0464eeaba6
commit 05c4763e49
18 changed files with 720 additions and 497 deletions
+13 -27
View File
@@ -37,13 +37,8 @@ Page({
workTotal: 0, // effective held seconds (excludes rest)
isResting: false,
phaseTip: '', // 阶段切换提示文本(如"最后一组!"/"休息 15 秒")
// 顶部循环进度面板:组进度胶囊点阵
circuitDots: [], // [1..sets],仅用于 wx:for 渲染,组数过多时不渲染
circuitShowDots: true, // 组数 > DOTS_MAX 时退化为线性进度条
circuitTotalWork: 0, // sets × holdPerSet,面板里作为"目标秒数"展示
dotsDone: 0, // 已完成组数
dotsActive: 0, // 正在进行的组序号(休息时为 0)
dotsNext: 0, // 休息时即将开始的组序号(工作时为 0)
// 组进度改由 progress-ring 外圈分段弧呈现,仅需传 sets/currentSet/resting,
// 页面不再维护点阵渲染数据(circuitDots/dotsDone 等已随旧面板一并删除)。
celebrationLevel: 'normal', // normal / first / milestone / record
celebrationMessage: '',
confettiPieces: [],
@@ -222,8 +217,8 @@ Page({
sets,
restPerSet: rest,
onTick: (t) => {
// 组进度点阵三态。休息阶段 CircuitTimer 的 setIndex 仍指向"刚做完
// 的那一组",所以 resting 时 setIndex 组算已完成、下一组标为 next
// 组进度语义与旧点阵一致:休息时 setIndex 仍指向"刚做完的那一组",
// 组件按 resting 自行推导已完成/进行中/下一组三态(外圈分段弧)
const resting = t.phase === 'resting'
this.setData({
currentSet: t.setIndex,
@@ -233,9 +228,6 @@ Page({
phaseElapsed: t.phaseElapsed,
workTotal: t.workTotal,
isResting: resting,
dotsDone: resting ? t.setIndex : Math.max(0, t.setIndex - 1),
dotsActive: resting ? 0 : t.setIndex,
dotsNext: resting ? t.setIndex + 1 : 0,
// Reuse the single-hold display fields so progress-ring / completion
// number keep working: the ring shows the CURRENT set's countdown.
remaining: t.phaseRemaining,
@@ -244,7 +236,9 @@ Page({
statusText: resting ? '休息中' : '撑住',
runningTip: resting ? '' : this.data.phaseTip,
tipType: 'phase',
tipIcon: this.data.icons.peopleFill
tipIcon: this.data.icons.peopleFill,
// 环内副标题:宏观进度(第几组)交给外圈分段弧,这里只报当前组位次。
ringSubText: `${t.setIndex}/${t.sets}`
})
this._circuitCue(t)
},
@@ -252,13 +246,9 @@ Page({
onComplete: () => this._onCircuitComplete()
})
// 点阵超过 DOTS_MAX 个就挤成头发丝了,退化成一条线性进度条。
const DOTS_MAX = 15
const dots = []
if (sets <= DOTS_MAX) {
for (let i = 1; i <= sets; i++) dots.push(i)
}
// 组数上限 50(见 _int 的 clamp),环形外圈 360° 空间充裕,即使 50 段也
// 不至于挤成线(旧点阵的 DOTS_MAX=15 是受横向卡片宽度限制,环形无此问题),
// 因此分段弧不做数量降级,页面也无须维护点阵渲染数据。
const durText = this._durationText(hold)
this.setData({
isCircuitMode: true,
@@ -268,12 +258,6 @@ Page({
circuitHold: hold,
circuitRest: rest,
circuitSessions: this._circuitCfg.sessionsPerWeek,
circuitDots: dots,
circuitShowDots: sets <= DOTS_MAX,
circuitTotalWork: sets * hold,
dotsDone: 0,
dotsActive: 0,
dotsNext: 0,
currentSet: 0,
totalSets: sets,
phase: 'idle',
@@ -294,7 +278,9 @@ Page({
showCompletion: false,
goalLine: `每组 ${durText} · 共 ${sets}`,
ringTicks: Math.min(hold, 60),
ringSubText: `每组 ${durText}`,
// idle 时环内副标题承载配置摘要(组数 + 休息时长),运行时由 onTick
// 换成"第 X/Y 组"。休息时长为 0 时不展示"休息"段。
ringSubText: `${sets}${rest > 0 ? ' · 休息 ' + rest + ' 秒' : ''}`,
statusText: '准备开始',
runningTip: '',
tipType: 'posture',