feat(timer): 训练中轮换姿势要领 + hint 字号调大
- 训练中 hint 每 12 秒轮换一条平板支撑姿势要领(收紧核心/肩膀放松/ 别塌腰/别憋气等 10 条),替代固定文案,帮助持续纠正姿势 - 姿势文案与间隔移入 config.js(postureTips/tipInterval),统一管理 - hint 字号 28rpx -> 34rpx,图标 32rpx -> 36rpx,更易看清 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,5 +26,25 @@ module.exports = {
|
|||||||
tts: {
|
tts: {
|
||||||
female: { voiceType: 501004, volume: 9, speed: 1 },
|
female: { voiceType: 501004, volume: 9, speed: 1 },
|
||||||
male: { voiceType: 502005, volume: 9, speed: 1 }
|
male: { voiceType: 502005, volume: 9, speed: 1 }
|
||||||
}
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 训练中轮换显示的平板支撑姿势要领。每条显示 tipInterval 秒后切换,
|
||||||
|
* 循环播放,帮助用户持续纠正姿势。增删改这里即可,无需改逻辑代码。
|
||||||
|
*/
|
||||||
|
postureTips: [
|
||||||
|
'收紧核心,腰部平直',
|
||||||
|
'肩膀沉肩,不要耸肩',
|
||||||
|
'目光看向地面,颈部保持自然中立',
|
||||||
|
'均匀呼吸,不要憋气',
|
||||||
|
'臀部与身体成一条直线',
|
||||||
|
'手肘位于肩正下方',
|
||||||
|
'腹部收紧,别塌腰',
|
||||||
|
'双腿伸直略微分开,脚跟后蹬',
|
||||||
|
'骨盆微微后倾,别翘臀',
|
||||||
|
'保持稳定,身体不晃'
|
||||||
|
],
|
||||||
|
|
||||||
|
/** 姿势要领每条显示的秒数 */
|
||||||
|
tipInterval: 10
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-1
@@ -5,7 +5,7 @@ const themeMod = require('../../utils/theme')
|
|||||||
const iconsMod = require('../../utils/icons')
|
const iconsMod = require('../../utils/icons')
|
||||||
const voice = require('../../utils/voice')
|
const voice = require('../../utils/voice')
|
||||||
const { countUp } = require('../../utils/countUp')
|
const { countUp } = require('../../utils/countUp')
|
||||||
|
const config = require('../../config')
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
theme: { primary: '#FF6B35', primaryLight: '#FF8C5A', primaryBg: '#FFF3ED', primaryRgb: '255,107,53' },
|
theme: { primary: '#FF6B35', primaryLight: '#FF8C5A', primaryBg: '#FFF3ED', primaryRgb: '255,107,53' },
|
||||||
@@ -18,6 +18,7 @@ Page({
|
|||||||
isCompleted: false,
|
isCompleted: false,
|
||||||
overtime: 0,
|
overtime: 0,
|
||||||
goalReached: false,
|
goalReached: false,
|
||||||
|
hintTip: (config.postureTips && config.postureTips[0]) || '',
|
||||||
todayTarget: 0,
|
todayTarget: 0,
|
||||||
planDay: 1,
|
planDay: 1,
|
||||||
isFreeMode: false,
|
isFreeMode: false,
|
||||||
@@ -96,6 +97,7 @@ Page({
|
|||||||
this._lastMinuteAt = 0
|
this._lastMinuteAt = 0
|
||||||
this._lastCountdown = 0
|
this._lastCountdown = 0
|
||||||
this._saving = false
|
this._saving = false
|
||||||
|
this._lastTipIndex = -1
|
||||||
this._clearVibrateTimers()
|
this._clearVibrateTimers()
|
||||||
|
|
||||||
this._timer = new Timer({
|
this._timer = new Timer({
|
||||||
@@ -104,6 +106,7 @@ Page({
|
|||||||
remaining: tick.remaining > 0 ? tick.remaining : 0,
|
remaining: tick.remaining > 0 ? tick.remaining : 0,
|
||||||
overtime: tick.remaining <= 0 ? tick.elapsed - this.data.duration : 0
|
overtime: tick.remaining <= 0 ? tick.elapsed - this.data.duration : 0
|
||||||
})
|
})
|
||||||
|
this._updateHintTip(tick)
|
||||||
this._remind(tick)
|
this._remind(tick)
|
||||||
},
|
},
|
||||||
onGoalReached: () => {
|
onGoalReached: () => {
|
||||||
@@ -118,6 +121,24 @@ Page({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate the posture cue in the hint area every config.tipInterval seconds.
|
||||||
|
* Only runs while still under the target (after goalReached the hint
|
||||||
|
* switches to the "goal reached" line). Skips setData when the index
|
||||||
|
* hasn't changed to avoid per-second re-renders.
|
||||||
|
*/
|
||||||
|
_updateHintTip(tick) {
|
||||||
|
if (tick.remaining <= 0) return
|
||||||
|
const tips = config.postureTips || []
|
||||||
|
if (tips.length === 0) return
|
||||||
|
const interval = config.tipInterval || 12
|
||||||
|
const idx = Math.floor(tick.elapsed / interval) % tips.length
|
||||||
|
if (idx !== this._lastTipIndex) {
|
||||||
|
this._lastTipIndex = idx
|
||||||
|
this.setData({ hintTip: tips[idx] })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per-tick reminders: vibration + voice prompts at 4 fixed points.
|
* Per-tick reminders: vibration + voice prompts at 4 fixed points.
|
||||||
* - halfway: once when elapsed reaches half of duration
|
* - halfway: once when elapsed reaches half of duration
|
||||||
@@ -431,6 +452,7 @@ Page({
|
|||||||
this._last10Signaled = false
|
this._last10Signaled = false
|
||||||
this._lastMinuteAt = 0
|
this._lastMinuteAt = 0
|
||||||
this._lastCountdown = 0
|
this._lastCountdown = 0
|
||||||
|
this._lastTipIndex = -1
|
||||||
this._clearVibrateTimers()
|
this._clearVibrateTimers()
|
||||||
this.setData({
|
this.setData({
|
||||||
showCompletion: false,
|
showCompletion: false,
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="hint-row" wx:elif="{{status === 'running'}}">
|
<view class="hint-row" wx:elif="{{status === 'running'}}">
|
||||||
<image class="hint-icon running-pulse" src="{{icons.likeFill}}" mode="aspectFit"></image>
|
<image class="hint-icon running-pulse" src="{{icons.likeFill}}" mode="aspectFit"></image>
|
||||||
<text class="hint-text running">保持姿势,核心收紧!</text>
|
<text class="hint-text running">{{hintTip}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="hint-row" wx:elif="{{status === 'paused'}}">
|
<view class="hint-row" wx:elif="{{status === 'paused'}}">
|
||||||
<image class="hint-icon" src="{{icons.notificationForbidFill}}" mode="aspectFit"></image>
|
<image class="hint-icon" src="{{icons.notificationForbidFill}}" mode="aspectFit"></image>
|
||||||
|
|||||||
@@ -102,8 +102,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hint-icon {
|
.hint-icon {
|
||||||
width: 32rpx;
|
width: 36rpx;
|
||||||
height: 32rpx;
|
height: 36rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.running-pulse {
|
.running-pulse {
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hint-text {
|
.hint-text {
|
||||||
font-size: 28rpx;
|
font-size: 34rpx;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user