feat: 完成训练分级庆祝 (normal/first/milestone/record)
- normal: 12 条彩带 - record (破个人纪录): 24 条 + 横幅"🏆 新纪录!X秒" - milestone (连击 7/14/30/50/100/365 天): 32 条 + 横幅 - first (首次训练): 40 条 + 横幅"🎉 第一次训练" 横幅 3.5s 后自动淡出,彩带时长随等级递增(2.4s → 4s)。
This commit is contained in:
+60
-1
@@ -20,6 +20,9 @@ Page({
|
||||
todayTarget: 0,
|
||||
planDay: 1,
|
||||
isFreeMode: false,
|
||||
celebrationLevel: 'normal', // normal / first / milestone / record
|
||||
celebrationMessage: '',
|
||||
confettiPieces: [],
|
||||
icons: iconsMod.build()
|
||||
},
|
||||
|
||||
@@ -92,7 +95,16 @@ Page({
|
||||
this._remind(tick)
|
||||
},
|
||||
onComplete: () => {
|
||||
this.setData({ status: 'completed', isCompleted: true })
|
||||
const elapsed = this._timer.elapsed
|
||||
const { level, message } = this._detectCelebrationLevel(elapsed)
|
||||
const confettiPieces = this._buildConfettiPieces(level)
|
||||
this.setData({
|
||||
status: 'completed',
|
||||
isCompleted: true,
|
||||
celebrationLevel: level,
|
||||
celebrationMessage: message,
|
||||
confettiPieces
|
||||
})
|
||||
const s = storage.getSettings()
|
||||
if (s.vibrate) {
|
||||
try { wx.vibrateLong() } catch (e) {}
|
||||
@@ -162,6 +174,53 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Detect which celebration level to show. Called at onComplete (BEFORE
|
||||
* saveRecord fires in finishTraining), so stats reflect pre-save state.
|
||||
* - first: user's very first training ever
|
||||
* - milestone: streak will hit a memorable number after this save
|
||||
* - record: this duration beats the user's previous max
|
||||
* - normal: everything else
|
||||
*/
|
||||
_detectCelebrationLevel(elapsed) {
|
||||
const stats = storage.getTotalStats()
|
||||
const streak = storage.getStreak()
|
||||
const nextStreak = (streak.lastDate && streak.count >= 1) ? streak.count + 1 : 1
|
||||
|
||||
if (stats.totalSessions === 0) {
|
||||
return { level: 'first', message: '🎉 第一次训练!坚持就是胜利' }
|
||||
}
|
||||
if ([7, 14, 30, 50, 100, 365].includes(nextStreak)) {
|
||||
return { level: 'milestone', message: `🔥 连续打卡 ${nextStreak} 天!` }
|
||||
}
|
||||
if (elapsed > stats.maxDuration) {
|
||||
return { level: 'record', message: `🏆 新纪录!${elapsed}秒` }
|
||||
}
|
||||
return { level: 'normal', message: '完成!' }
|
||||
},
|
||||
|
||||
/**
|
||||
* Build the confetti piece list. More pieces + more colors for higher
|
||||
* celebration levels. Pieces are pre-positioned so the layout doesn't
|
||||
* shift when wx:for renders them.
|
||||
*/
|
||||
_buildConfettiPieces(level) {
|
||||
const counts = { normal: 12, record: 24, milestone: 32, first: 40 }
|
||||
const count = counts[level] || counts.normal
|
||||
const palette = ['#FF6B35', '#FFD93D', '#4CAF50', '#2979FF', '#EC407A', '#7E57C2', '#FF8C5A', '#5C9CFF', '#6EC072', '#A08AD6']
|
||||
const pieces = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
pieces.push({
|
||||
left: Math.round((i / count) * 96 + 2),
|
||||
color: palette[i % palette.length],
|
||||
delay: Math.round(Math.random() * 300) / 1000,
|
||||
rotate: Math.round((Math.random() * 90) - 45),
|
||||
size: level === 'first' ? 40 : level === 'milestone' ? 36 : 32
|
||||
})
|
||||
}
|
||||
return pieces
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
voice.stop()
|
||||
voice.destroy()
|
||||
|
||||
Reference in New Issue
Block a user