当前位置: 首页 > news >正文

全局计时器、智能提醒与UI交互实现

本文分享「灶台导航」小程序中多菜谱烹饪的运行机制,包括全局计时器设计、智能提醒触发逻辑以及页面交互的完整实现。


一、全局计时器实现

1.1 计时器完整代码

这个计时器类支持开始、暂停、继续、停止,并且支持注册多个回调函数:

/** * 多菜谱全局计时器 * 核心功能:统一时间基准、支持暂停/继续、每秒通知所有监听者 */ class GlobalCookTimer { constructor() { this.startTime = null // 开始时间戳(毫秒) this.elapsedTime = 0 // 已经过的秒数 this.intervalId = null // 定时器ID this.isRunning = false // 是否运行中 this.isPaused = false // 是否暂停中 this.pauseTime = 0 // 暂停时的时间戳 this.callbacks = [] // 回调函数列表 } /** * 开始计时 */ start() { this.startTime = Date.now() this.isRunning = true this.isPaused = false this.intervalId = setInterval(() => { this.tick() }, 1000) } /** * 暂停 */ pause() { if (!this.isRunning || this.isPaused) return this.isPaused = true this.pauseTime = Date.now() clearInterval(this.intervalId) }
/** * 继续(核心:补偿暂停时间) * 原理:startTime 向后移动 pauseDuration,仿佛暂停从未发生 */ resume() { if (!this.isPaused) return // 计算暂停了多久 const pauseDuration = Date.now() - this.pauseTime // 调整开始时间,补偿暂停时长 this.startTime += pauseDuration this.isPaused = false this.intervalId = setInterval(() => { this.tick() }, 1000) } /** * 停止 */ stop() { clearInterval(this.intervalId) this.isRunning = false this.isPaused = false }
/** * 每秒执行一次,更新经过时间并通知回调 */ tick() { this.elapsedTime = Math.floor((Date.now() - this.startTime) / 1000) this.callbacks.forEach(cb => cb(this.elapsedTime)) } /** * 注册回调 */ onTick(callback) { this.callbacks.push(callback) }
/** * 获取当前时间(支持暂停时正确返回) */ getElapsed() { if (this.isPaused) { return Math.floor((this.pauseTime - this.startTime) / 1000) } return Math.floor((Date.now() - this.startTime) / 1000) } /** * 销毁 */ destroy() { this.stop() this.callbacks = [] } }

1.2 暂停补偿原理图解

用户暂停10分钟的场景: 暂停前: startTime = 12:00:00 当前经过时间 = 300秒 当前真实时间 = 12:05:00 用户点击暂停,10分钟后点击继续: 暂停时长 = 600秒 新startTime = 12:00:00 + 600 = 12:10:00 恢复后计算经过时间: 当前真实时间 = 12:15:00 经过时间 = 12:15:00 - 12:10:00 = 300秒 ✅ 效果:仿佛那10分钟从未存在过,计时器继续从300秒开始

1.3 页面集成代码

// pages/cook/cook.js Page({ data: { schedule: null, elapsedSeconds: 0, currentTasks: [], completedSteps: [] }, globalTimer: null, onLoad(options) { const recipeIds = JSON.parse(options.recipes || '[]') this.loadAndCalculate(recipeIds) }, onUnload() { // 页面销毁时清理计时器,防止内存泄漏 if (this.globalTimer) { this.globalTimer.destroy() } }, async loadAndCalculate(recipeIds) { const recipes = await this.loadRecipes(recipeIds) const schedule = calculateSchedule(recipes) this.setData({ schedule }) this.initTimer(schedule) }, initTimer(schedule) { this.globalTimer = new GlobalCookTimer() this.globalTimer.onTick((elapsed) => { this.onTimerTick(elapsed) }) this.globalTimer.start() }, onTimerTick(elapsed) { const { schedule } = this.data this.setData({ elapsedSeconds: elapsed }) // 获取当前应该执行的任务 const currentTasks = this.getCurrentTasks(elapsed) if (currentTasks.length > 0) { this.setData({ currentTasks }) this.checkNewTasks(currentTasks) } this.checkCompletedTasks(elapsed) // 检查是否全部完成 if (elapsed >= schedule.totalDuration) { this.onAllComplete() } }, /** * 获取当前时间点正在进行的任务 * 核心逻辑:判断 elapsed 是否在步骤的 [start, end) 区间内 */ getCurrentTasks(elapsed) { const tasks = [] this.data.schedule.recipes.forEach(recipe => { recipe.steps.forEach(step => { if (elapsed >= step.globalStartTime && elapsed < step.globalEndTime) { tasks.push({ recipeId: recipe.recipeId, recipeName: recipe.recipeName, step, remaining: step.globalEndTime - elapsed // 剩余秒数 }) } }) }) return tasks } })

二、提醒机制实现

2.1 提醒管理器完整代码

/** * 提醒管理器 * 功能:检测关键节点,触发语音提醒,防止重复提醒 */ class ReminderManager { constructor(ttsQueue) { this.ttsQueue = ttsQueue // 语音队列,处理异步播放 this.reminded = new Set() // 已提醒过的任务ID,用于去重 } /** * 检查是否需要提醒(每秒调用一次) */ check(elapsed, schedule) { // 检查即将开始的任务(提前30秒提醒) schedule.timeline.forEach(event => { if (event.type === 'start') { const timeToStart = event.time - elapsed if (timeToStart > 0 && timeToStart <= 30 && !this.reminded.has(`start-${event.time}`)) { this.reminded.add(`start-${event.time}`) this.remindTaskStart(event) } } }) // 检查即将结束的任务(提前10秒提醒) schedule.timeline.forEach(event => { if (event.type === 'end') { const timeToEnd = event.time - elapsed if (timeToEnd > 0 && timeToEnd <= 10 && !this.reminded.has(`end-${event.time}`)) { this.reminded.add(`end-${event.time}`) this.remindTaskEnd(event) } } }) } remindTaskStart(event) { const text = `${event.recipeName}即将开始第${event.step.order}步` this.ttsQueue.add(text) } remindTaskEnd(event) { const text = `${event.recipeName}第${event.step.order}步即将完成` this.ttsQueue.add(text) } reset() { this.reminded.clear() } }

2.2 提前量设计说明

提醒类型提前量设计原因
任务开始30秒给用户30秒准备时间,比如洗手、拿食材
任务结束10秒防止烧干锅,10秒足够用户走到灶台前
剩余时间10分钟/5分钟/1分钟整点提醒,让用户把握整体进度

2.3 周期性时间提醒

/** * 周期性时间提醒 * 在关键剩余时间点提醒用户 */ function setupPeriodicReminder(timer, schedule) { // 关键时间节点(秒):10分钟、5分钟、1分钟、30秒、10秒 const reminderPoints = [600, 300, 60, 30, 10] const reminded = new Set() timer.onTick(elapsed => { const remaining = schedule.totalDuration - elapsed reminderPoints.forEach(point => { // 当剩余时间首次小于等于某个节点时触发提醒 if (remaining <= point && remaining > point - 5 && !reminded.has(point)) { reminded.add(point) announceRemaining(remaining) } }) }) } function announceRemaining(seconds) { let text if (seconds >= 600) { text = `还有${Math.floor(seconds / 60)}分钟` } else if (seconds >= 60) { text = `还有${Math.floor(seconds / 60)}分钟` } else { text = `还有${seconds}秒` } ttsQueue.add(text) }

三、UI 展示

3.1 页面模板代码

<!-- 多菜谱烹饪页面 --> <view class="multi-cook-container"> <!-- 时间概览 --> <view class="time-overview"> <text class="elapsed">{{elapsedDisplay}}</text> <text class="total">/ {{totalDurationDisplay}}</text> </view> <!-- 进度条 --> <view class="progress-bar"> <view class="progress-fill" style="width: {{progress}}%"></view> </view> <!-- 当前任务列表 --> <view class="current-tasks"> <view class="task-card" wx:for="{{currentTasks}}" wx:key="recipeId"> <view class="task-header"> <text class="recipe-name">{{item.recipeName}}</text> <text class="step-label">步骤 {{item.step.order}}</text> </view> <view class="task-content">{{item.step.content}}</view> <view class="task-footer"> <text class="remaining">剩余 {{item.remaining}}秒</text> </view> </view> </view> <!-- 控制按钮 --> <view class="controls"> <button wx:if="{{!isPaused}}" bindtap="onPause">暂停</button> <button wx:if="{{isPaused}}" bindtap="onResume">继续</button> </view> </view>

3.2 UI 交互说明

UI 元素数据绑定更新频率作用
时间概览elapsedDisplay每秒让用户知道当前进度
进度条progress%每秒视觉化整体进度
任务卡片currentTasks任务变化时显示当前该做什么
剩余时间remaining每秒紧迫感提示
控制按钮isPaused点击时暂停/继续计时

3.3 任务卡片显示逻辑

  • 可以显示多个卡片:比如红烧肉在炖(wait类型),同时番茄蛋汤在准备(cook类型),两个任务可以并行

  • 剩余时间倒计时:每秒递减,让用户感知紧迫程度

  • 完成任务自动移除:当 elapsed >= globalEndTime 时,该任务卡片消失


四、总结

模块核心职责关键代码设计要点
全局计时器统一时间基准,支持暂停GlobalCookTimer 类暂停补偿算法
任务检测实时判断当前该做什么getCurrentTasks()区间比较 + 状态机
提醒机制关键节点语音播报ReminderManager 类提前量 + 去重队列
UI交互信息展示与用户控制WXML + 数据绑定多任务卡片 + 进度条

整套系统让用户可以"无脑"跟着指引走,系统自动告诉你接下来该做什么、什么时候做、还剩多久,大大降低了多菜同时烹饪的心智负担。

http://www.jsqmd.com/news/1001707/

相关文章:

  • 解密Apollo配置中心的高可用设计:从长轮询到本地缓存,你的配置真的安全吗?
  • 从Q_PROPERTY到MVVM:手把手教你用属性系统重构臃肿的Qt业务逻辑
  • SpringBoot 3.2项目实战:除了虚拟线程,JDK21的这些新特性更值得你关注
  • 孤舟笔记 分布式与微服务篇二十四 IaaS、PaaS、SaaS有啥区别?三个字母搞懂云计算三层模型
  • 手机号找回QQ号完整指南:3分钟破解账号记忆难题
  • Quake3e:现代图形API如何重塑经典竞技场引擎的技术架构
  • VC++实现的IF-ELSE语句LL(1)语法分析与四元式生成工程
  • 从上传到播放:手把手模拟一次YouTube视频的‘奇幻漂流’(附FFmpeg转码命令实操)
  • CAD二次开发避坑指南:VBA选择集过滤时,为什么你的‘*Polyline’选不中所有多段线?
  • 今天摸鱼了吗APP开发实战:基于HarmonyOS API 24的多层Stack与定时器应用
  • Flutter 实战:simple_paint 手绘画板的手势采样、CustomPainter 绘制与鸿蒙适配解析
  • 突破60帧枷锁:原神帧率解锁工具完全指南
  • NPOI 2.5.1.0 .NET 4.0 全依赖二进制库包(含XML文档与Excel全格式支持)
  • 2026江苏技术过硬宣传片制作机构排行 核心维度实测对比 - 奔跑123
  • 从‘烤机’到‘炼丹’:聊聊不同场景下CUDA线程配置的实战经验(附V100/A100对比)
  • OpenCore Configurator:黑苹果引导配置的终极可视化工具指南
  • 性价比高的3%AFFF/AR抗溶性水成膜泡沫灭火剂厂家推荐:浙江金瑞恒守护能源安全 - 品牌速递
  • 国内售后完善的教学能力比赛拍摄服务商综合排行2026 - 奔跑123
  • NXP i.MX 6 SABRE开发板:从硬件参考设计到产品实战全解析
  • ARM7汽车MCU MAC7100架构解析与eDMA、FlexCAN实战应用
  • 面向对象:this关键字;构造器
  • Claude进入受监管系统前,接入层应该先怎么设计
  • 2026年AI精准获客TOP5技巧,让您的业务增长不再难 - 轩铭卿
  • CRISPR-Cas9新玩法:像调光开关一样,用uORF精细调控植物基因表达
  • 2026携号转网API选型全指南:直连接口、代码示例与生产环境踩坑实录
  • Win7系统下惠普M1005激光一体机即装即用驱动包(32/64位双版)
  • Typora插件终极指南:70+免费功能让Markdown写作效率提升300%
  • 顺序表(动态数组)深度精讲,从零手写实现、扩容机制、边界处理、增删查改全解析与复杂度分析
  • 终极指南:5分钟快速上手layerdivider AI图像分层工具
  • 浙江金瑞恒稳居6%AFFF/AR抗溶性水成膜消防泡沫液品牌前十名,包裹保护泡沫 - 品牌速递