微信小程序篮球计分板实战:计时、暂停和撤销怎么设计
做篮球计分板时最容易被低估的是状态一致性。比分加减并不难真正容易出错的是这些场景计时正在运行时用户切到后台记录暂停后主计时和进攻计时没有一起停误点一次两分球撤销后总分恢复了但节比分和操作记录没有恢复。如果只围绕页面按钮写逻辑功能越加越难维护。更稳妥的做法是先把比赛状态、计时器和业务事件分开。## 计时显示不是状态源计分板通常需要保存以下字段jsconst matchState {timerMode: down, // down倒计时up正计时clockSeconds: 600,clockRunning: false,period: 1,homeScore: 0,awayScore: 0,events: []}页面上的 10:00 只是 clockSeconds 的格式化结果不应成为业务状态。切换正计时和倒计时时也只需要重置秒数jsfunction resetClock(mode, periodMinutes) {return mode down ? periodMinutes * 60 : 0}function stepClock(mode, seconds) {return mode down? Math.max(0, seconds - 1): seconds 1}这样显示格式、节次设置和比赛结果保存都围绕同一份秒数工作不必从文本中反向解析时间。## 不要假设 setInterval 永远准时小程序进入后台后定时器可能暂停或延迟。如果重新显示页面时继续沿用离开前的数值比赛时间就会少走一段。处理方法是页面隐藏时记下时间戳和运行状态并停止定时器页面恢复时计算实际经过的秒数。jsonHide() {this.hiddenAt Date.now()this.clockWasRunning this.data.clockRunningthis.stopClock()},onShow() {if (!this.hiddenAt || !this.clockWasRunning) returnconst elapsed Math.floor((Date.now() - this.hiddenAt) / 1000)const next this.data.timerMode down? Math.max(0, this.data.clockSeconds - elapsed): this.data.clockSeconds elapsedthis.setData({ clockSeconds: next })this.startClock()}倒计时恢复到零时不应再次启动定时器而应直接进入本节结束处理。进攻计时也要使用同样的恢复规则并且不能越过主计时的结束点。## 暂停不是弹出一个对话框一次球队暂停至少包含四个动作- 增加该队暂停次数- 写入一条带节次和时间的比赛事件- 停止主计时- 停止进攻计时。这四步应放在同一个业务入口里。只显示“已暂停”的弹窗却忘记停止其中一个计时器现场记录很快就会对不上。jsfunction handleTeamTimeout(side) {saveUndoSnapshot(暂停)increaseTimeoutCount(side)appendEvent({period: state.period,time: getEventTime(),team: side,action: 暂停})stopMainClock()stopShotClock()}是否继续读秒、是否播放暂停音乐可以交给后续交互确认。比赛状态先停稳再处理声音和提示业务逻辑会清楚很多。## 撤销要恢复一组关联状态误点得分时只把总分减回去并不够。一次得分可能同时改变总分、单节比分、球员得分和最近操作。简单可靠的方式是在执行可撤销操作前保存快照jsfunction pushUndoSnapshot(label) {undoStack.push({label,data: {homeScore: state.homeScore,awayScore: state.awayScore,periodScores: clone(state.periodScores),playerStats: clone(state.playerStats),events: clone(state.events)}})undoStack undoStack.slice(-30)}function undoLastAction() {const snapshot undoStack.pop()if (!snapshot) returnrestoreState(snapshot.data)saveMatchRecord()}快照只保存会被本次操作修改的字段。实时比赛时间通常不跟随一次比分撤销回退否则用户撤销两分球时计时也可能跳回几秒前。如果后续需要多人协同记录或云端审计可以把快照升级为命令模式每次操作保存动作类型、前值、后值、操作者和时间再为每类动作编写对应的逆操作。**操作记录要能回答“为什么变成这样”**最近操作不能只显示“主队 2”。更有用的事件结构包括js{id: event-id,period: 2,time: 03:18,team: 主队,action: #7 李明 得分 2,score: 36-31}这条记录能帮助现场人员确认三件事发生在哪一节、当时比赛时间是多少、操作后的比分是多少。出现争议时也能快速定位需要撤销或核对的动作。## 上线前至少测试这些场景1. 正计时和倒计时分别运行、暂停、恢复、归零。2. 计时过程中切后台十秒再返回前台。3. 记录球队暂停后主计时和进攻计时是否同时停止。4. 连续记录两分、犯规和换人再逐次撤销。5. 撤销栈为空时是否给出明确提示。6. 一节结束时是否阻止重复蜂鸣和重复结算。7. 页面退出或比赛结束时最后状态是否已经保存。篮球计分板不是单纯的数字面板。把时间当作独立状态把暂停当作完整业务动作把撤销设计成关联状态恢复后续增加节次、犯规、球员统计和赛果保存时会更容易控制。完整的现场操作顺序可参考[篮球比赛现场计分指南]