"use strict"; const common_vendor = require("../../common/vendor.js"); const training_hooks_useMetronome = require("../hooks/useMetronome.js"); if (!Math) { CrushCanvas(); } const CrushCanvas = () => "./components/crush-canvas.js"; const TUTORIAL_VIDEO_URL = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/video/20260528/20260528173911bf6f93386.mp4"; const TUTORIAL_POSTER_URL = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/images/20260529/202605291030231aed12802.jpg"; const BGM_URL = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260528/20260528093709c669f4659.mp3"; const _sfc_defineComponent = common_vendor.defineComponent({ __name: "grip-ring", setup(__props) { const GRIP_PRESETS = [ { id: "light", bpm: 60, met: 2.5, label: "轻度", desc: "热身 · 恢复" }, { id: "medium", bpm: 80, met: 3.5, label: "中度", desc: "日常 · 锻炼" }, { id: "heavy", bpm: 100, met: 4.5, label: "重度", desc: "强化 · 挑战" } ]; const TARGET_OPTIONS = [ { minutes: 0, label: "自由" }, { minutes: 5, label: "5 分钟" }, { minutes: 10, label: "10 分钟" }, { minutes: 15, label: "15 分钟" } ]; const REWARDS = [ { emoji: "🥚", title: "捏碎鸡蛋", desc: "握力不错!", itemType: "egg", sound: "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260528/202605280937077bad76716.mp3", minReps: 8, maxReps: 12 }, { emoji: "🌰", title: "捏碎核桃", desc: "力量惊人!", itemType: "walnut", sound: "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260528/20260528093707b021f5794.mp3", minReps: 15, maxReps: 20 }, { emoji: "🥫", title: "捏扁易拉罐", desc: "太强了!", itemType: "can", sound: "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260528/20260528093707ebd4d5733.mp3", minReps: 25, maxReps: 30 }, { emoji: "🎈", title: "捏爆气球", desc: "完美!", itemType: "balloon", sound: "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260528/20260528093707016f07427.mp3", minReps: 40, maxReps: 50 } ]; const FOOD_SUGAR_TABLE = [ { emoji: "🍬", name: "糖果", sugar: 95, unit: "颗", weight: 5 }, // 1颗糖果约5g { emoji: "🍪", name: "饼干", sugar: 65, unit: "块", weight: 10 }, // 1块饼干约10g { emoji: "🍫", name: "巧克力", sugar: 51.5, unit: "块", weight: 50 }, // 1块巧克力约50g { emoji: "🍎", name: "苹果", sugar: 10.3, unit: "个", weight: 200 }, // 1个苹果约200g { emoji: "🍌", name: "香蕉", sugar: 12.2, unit: "根", weight: 120 }, // 1根香蕉约120g { emoji: "🍚", name: "米饭", sugar: 25.9, unit: "碗", weight: 150 }, // 1碗米饭约150g { emoji: "🥤", name: "可乐", sugar: 10.6, unit: "罐", weight: 330 } // 1罐可乐330ml ]; const currentPreset = common_vendor.ref("medium"); const targetMinutes = common_vendor.ref(0); const crushSignal = common_vendor.ref(null); const showRewardPopup = common_vendor.ref(false); const showSummary = common_vendor.ref(false); const currentReward = common_vendor.ref(REWARDS[0]); const crushAudioCtx = common_vendor.ref(null); const bgmAudioCtx = common_vendor.ref(null); const bgmEnabled = common_vendor.ref(true); const totalReps = common_vendor.ref(0); const elapsedSeconds = common_vendor.ref(0); const totalSugar = common_vendor.ref(0); const beatCount = common_vendor.ref(0); const rewardIndex = common_vendor.ref(0); const nextRewardAt = common_vendor.ref(0); const startTime = common_vendor.ref(0); const timerInterval = common_vendor.ref(null); const preset = common_vendor.computed(() => GRIP_PRESETS.find((p) => p.id === currentPreset.value) || GRIP_PRESETS[1]); const currentBpm = common_vendor.computed(() => preset.value.bpm); const currentMet = common_vendor.computed(() => preset.value.met); const targetExpanded = common_vendor.ref(false); const currentTargetLabel = common_vendor.computed(() => { const opt = TARGET_OPTIONS.find((t) => t.minutes === targetMinutes.value); return opt ? opt.label : "自由"; }); const benefitExpanded = common_vendor.ref(false); const TRAINING_BENEFIT = { muscles: ["前臂屈肌", "手部小肌群", "握力"], effect: "增强握力与前臂肌肉耐力,促进手部血液循环,缓解手指与腕部僵硬,适合日常碎片化锻炼及手部功能康复。" }; const metronome = training_hooks_useMetronome.useMetronome({ initialBpm: currentBpm.value, accentEvery: 2, silent: true, // 移除木鱼节奏音,仅保留节拍驱动(视觉缩放/次数统计) onBeat }); const { isPlaying } = metronome; common_vendor.watch(isPlaying, (playing) => { const videoCtx = common_vendor.index.createVideoContext("gripDemoVideo"); if (!videoCtx) return; if (playing) { videoCtx.play(); } else { videoCtx.pause(); } }); const intervalMs = common_vendor.computed(() => 6e4 / currentBpm.value); const hasStats = common_vendor.computed(() => isPlaying.value || totalReps.value > 0); const isPaused = common_vendor.computed(() => !isPlaying.value && totalReps.value > 0 && !showSummary.value); const remainingSeconds = common_vendor.computed(() => { if (targetMinutes.value === 0) return null; const total = targetMinutes.value * 60; return Math.max(0, total - elapsedSeconds.value); }); const centerHintText = common_vendor.computed(() => isPaused.value ? "点击继续" : "点击开始"); const statusText = common_vendor.computed(() => { if (isPlaying.value) return "训练中"; if (isPaused.value) return "已暂停"; return "待开始"; }); const shareTitle = common_vendor.computed(() => { if (totalReps.value === 0) return "握力环训练 · 一起练起来"; return `刚刚完成 ${totalReps.value} 次握力训练,约消耗 ${totalSugar.value.toFixed(1)}g 糖分 💪`; }); const rootStyle = common_vendor.computed(() => ({ "--beat-duration": `${intervalMs.value}ms` })); const sugarComparisons = common_vendor.computed(() => { if (totalSugar.value === 0) return []; const results = []; for (const food of FOOD_SUGAR_TABLE) { const foodSugar = food.sugar * food.weight / 100; const count = totalSugar.value / foodSugar; if (count >= 0.1 && count <= 50) { results.push({ emoji: food.emoji, name: food.name, count: count >= 10 ? Math.round(count).toString() : count.toFixed(1), unit: food.unit }); } } return results.slice(0, 3); }); function onBeat() { beatCount.value++; if (beatCount.value % 2 === 0) { totalReps.value++; if (totalReps.value >= nextRewardAt.value && rewardIndex.value < REWARDS.length) { triggerReward(); } } } function calculateSugar(seconds) { const hours = seconds / 3600; const weight = 60; const calories = currentMet.value * weight * hours; return calories / 4; } function formatTime(seconds) { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, "0")}`; } function triggerReward() { const reward = REWARDS[rewardIndex.value]; currentReward.value = reward; crushSignal.value = { type: reward.itemType, nonce: Date.now() }; playCrushSound(reward.sound); triggerHaptic(); rewardIndex.value++; if (rewardIndex.value < REWARDS.length) { const nextReward = REWARDS[rewardIndex.value]; const range = nextReward.maxReps - nextReward.minReps; nextRewardAt.value = totalReps.value + nextReward.minReps + Math.floor(Math.random() * (range + 1)); } setTimeout(() => { showRewardPopup.value = true; }, 1200); } function closeRewardPopup() { showRewardPopup.value = false; } function triggerHaptic() { const buzz = (type) => { common_vendor.index.vibrateShort({ type, fail: () => { try { common_vendor.index.vibrateShort({}); } catch (_) { } } }); }; buzz("heavy"); setTimeout(() => buzz("medium"), 120); } function playCrushSound(sound) { if (!sound) return; if (crushAudioCtx.value) { try { crushAudioCtx.value.destroy(); } catch (_) { } } crushAudioCtx.value = common_vendor.index.createInnerAudioContext(); crushAudioCtx.value.src = sound; crushAudioCtx.value.obeyMuteSwitch = false; crushAudioCtx.value.volume = 0.6; try { crushAudioCtx.value.play(); } catch (_) { } } function startBgm() { if (!bgmEnabled.value) return; if (bgmAudioCtx.value) { try { bgmAudioCtx.value.play(); } catch (_) { } return; } bgmAudioCtx.value = common_vendor.index.createInnerAudioContext(); bgmAudioCtx.value.src = BGM_URL; bgmAudioCtx.value.loop = true; bgmAudioCtx.value.obeyMuteSwitch = false; bgmAudioCtx.value.volume = 0.4; try { bgmAudioCtx.value.play(); } catch (_) { } } function stopBgm() { if (bgmAudioCtx.value) { try { bgmAudioCtx.value.pause(); } catch (_) { } } } function toggleBgm() { bgmEnabled.value = !bgmEnabled.value; if (bgmEnabled.value && isPlaying.value) { startBgm(); } else { stopBgm(); } } function onPresetTap(id) { if (isPlaying.value) return; currentPreset.value = id; metronome.setBpm(preset.value.bpm); } function onTargetTap(minutes) { if (isPlaying.value) return; targetMinutes.value = minutes; } function onTogglePlay() { if (isPlaying.value) { pauseTraining(); } else { if (totalReps.value === 0) { resetStats(); } startTrainingInternal(); } } function startTrainingInternal() { metronome.start(); startBgm(); startTimer(); common_vendor.index.setKeepScreenOn({ keepScreenOn: true }); } function pauseTraining() { metronome.stop(); stopBgm(); stopTimer(); common_vendor.index.setKeepScreenOn({ keepScreenOn: false }); } function onEndTraining() { if (totalReps.value === 0) return; pauseTraining(); showSummary.value = true; } function dismissSummary() { showSummary.value = false; resetStats(); } function onFinish() { showSummary.value = false; resetStats(); } function onRestart() { showSummary.value = false; resetStats(); startTrainingInternal(); } function resetStats() { totalReps.value = 0; elapsedSeconds.value = 0; totalSugar.value = 0; beatCount.value = 0; rewardIndex.value = 0; startTime.value = Date.now(); const firstReward = REWARDS[0]; const range = firstReward.maxReps - firstReward.minReps; nextRewardAt.value = firstReward.minReps + Math.floor(Math.random() * (range + 1)); } function startTimer() { if (timerInterval.value) return; startTime.value = Date.now() - elapsedSeconds.value * 1e3; timerInterval.value = setInterval(() => { elapsedSeconds.value = Math.floor((Date.now() - startTime.value) / 1e3); totalSugar.value = calculateSugar(elapsedSeconds.value); if (targetMinutes.value > 0 && elapsedSeconds.value >= targetMinutes.value * 60) { onEndTraining(); } }, 1e3); } function stopTimer() { if (timerInterval.value) { clearInterval(timerInterval.value); timerInterval.value = null; } } common_vendor.onShow(() => { metronome.preload(); }); common_vendor.onShareAppMessage(() => ({ title: shareTitle.value, path: "/training/pages/grip-ring", imageUrl: TUTORIAL_POSTER_URL })); common_vendor.onShareTimeline(() => ({ title: shareTitle.value, imageUrl: TUTORIAL_POSTER_URL })); function cleanupAllMedia() { var _a; try { metronome.stop(); } catch (_) { } stopTimer(); common_vendor.index.setKeepScreenOn({ keepScreenOn: false }); try { (_a = common_vendor.index.createVideoContext("gripDemoVideo")) == null ? void 0 : _a.pause(); } catch (_) { } if (bgmAudioCtx.value) { try { bgmAudioCtx.value.stop(); } catch (_) { } try { bgmAudioCtx.value.destroy(); } catch (_) { } bgmAudioCtx.value = null; } if (crushAudioCtx.value) { try { crushAudioCtx.value.stop(); } catch (_) { } try { crushAudioCtx.value.destroy(); } catch (_) { } crushAudioCtx.value = null; } } common_vendor.onHide(cleanupAllMedia); common_vendor.onUnload(cleanupAllMedia); return (_ctx, _cache) => { return common_vendor.e({ a: common_vendor.t(statusText.value), b: common_vendor.unref(isPlaying) ? 1 : "", c: isPaused.value ? 1 : "", d: common_vendor.p({ ["crush-signal"]: crushSignal.value }), e: TUTORIAL_VIDEO_URL, f: TUTORIAL_POSTER_URL, g: !common_vendor.unref(isPlaying) }, !common_vendor.unref(isPlaying) ? { h: common_vendor.t(centerHintText.value) } : { i: common_vendor.t(currentBpm.value) }, { j: common_vendor.o(onTogglePlay, "9b"), k: common_vendor.unref(isPlaying) ? 1 : "", l: common_vendor.t(bgmEnabled.value ? "🎵" : "🔇"), m: common_vendor.o(toggleBgm, "6e"), n: remainingSeconds.value !== null && hasStats.value }, remainingSeconds.value !== null && hasStats.value ? { o: common_vendor.t(formatTime(remainingSeconds.value)) } : {}, { p: common_vendor.unref(isPlaying) }, common_vendor.unref(isPlaying) ? {} : {}, { q: common_vendor.f(GRIP_PRESETS, (p, k0, i0) => { return { a: common_vendor.t(p.label), b: common_vendor.t(p.bpm), c: p.id, d: currentPreset.value === p.id ? 1 : "", e: common_vendor.o(($event) => onPresetTap(p.id), p.id) }; }), r: common_vendor.unref(isPlaying) ? 1 : "", s: common_vendor.t(currentTargetLabel.value), t: common_vendor.t(targetExpanded.value ? "▲" : "▼"), v: common_vendor.o(($event) => targetExpanded.value = !targetExpanded.value, "16"), w: targetExpanded.value }, targetExpanded.value ? { x: common_vendor.f(TARGET_OPTIONS, (t, k0, i0) => { return { a: common_vendor.t(t.label), b: t.minutes, c: targetMinutes.value === t.minutes ? 1 : "", d: common_vendor.o(($event) => onTargetTap(t.minutes), t.minutes) }; }), y: common_vendor.unref(isPlaying) ? 1 : "" } : {}, { z: common_vendor.t(benefitExpanded.value ? "收起 ▲" : "展开 ▼"), A: common_vendor.o(($event) => benefitExpanded.value = !benefitExpanded.value, "ee"), B: benefitExpanded.value }, benefitExpanded.value ? { C: common_vendor.f(TRAINING_BENEFIT.muscles, (m, k0, i0) => { return { a: common_vendor.t(m), b: m }; }), D: common_vendor.t(TRAINING_BENEFIT.effect) } : {}, { E: isPaused.value }, isPaused.value ? { F: common_vendor.o(onTogglePlay, "8a"), G: common_vendor.o(onEndTraining, "d0") } : {}, { H: common_vendor.unref(isPlaying) ? 1 : "", I: isPaused.value ? 1 : "", J: common_vendor.s(rootStyle.value), K: showRewardPopup.value }, showRewardPopup.value ? { L: common_vendor.t(currentReward.value.emoji), M: common_vendor.t(currentReward.value.title), N: common_vendor.t(currentReward.value.desc), O: common_vendor.t(totalReps.value), P: common_vendor.o(closeRewardPopup, "ae"), Q: common_vendor.o(() => { }, "5b"), R: common_vendor.o(closeRewardPopup, "1a") } : {}, { S: showSummary.value }, showSummary.value ? common_vendor.e({ T: common_vendor.t(totalReps.value), U: common_vendor.t(formatTime(elapsedSeconds.value)), V: common_vendor.t(totalSugar.value.toFixed(1)), W: sugarComparisons.value.length > 0 }, sugarComparisons.value.length > 0 ? { X: common_vendor.f(sugarComparisons.value, (food, idx, i0) => { return { a: common_vendor.t(food.emoji), b: common_vendor.t(food.count), c: common_vendor.t(food.unit), d: common_vendor.t(food.name), e: idx }; }) } : {}, { Y: common_vendor.o(onRestart, "78"), Z: common_vendor.o(onFinish, "a1"), aa: common_vendor.o(() => { }, "c4"), ab: common_vendor.o(dismissSummary, "77") }) : {}); }; } }); _sfc_defineComponent.__runtimeHooks = 6; const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_defineComponent, [["__scopeId", "data-v-cebdebdc"]]); wx.createPage(MiniProgramPage); //# sourceMappingURL=../../../.sourcemap/mp-weixin/training/pages/grip-ring.js.map