"use strict"; const common_vendor = require("../../../common/vendor.js"); const _sfc_main = { props: { isPlaying: { type: Boolean, default: false }, bpm: { type: Number, default: 110 } }, data() { return { canvasWidth: 320, canvasHeight: 400, ctx: null, timer: null, renderRunning: false, // Interaction State (Spring Physics) coreScale: 1, coreTargetScale: 1, coreVelocity: 0, pressed: false, // Animation Physics (Time) time: 0, lastFrameTime: 0, // Metronome State lastBeatTime: 0, // Energy Waves waveAmplitudeMultiplier: 1, // Shockwaves shockwaves: [], // Rotation for outer ring ringAngle: 0 }; }, mounted() { this.initCanvas(); }, beforeUnmount() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } this.renderRunning = false; }, watch: { isPlaying(newVal) { if (newVal) { this.waveAmplitudeMultiplier = 1; this.ensureRenderLoop(); } } }, methods: { initCanvas() { setTimeout(() => { const query = common_vendor.index.createSelectorQuery().in(this); query.select(".walker-canvas-container").boundingClientRect((data) => { if (data && data.width > 0) { this.canvasWidth = data.width; this.canvasHeight = data.height > 100 ? data.height : data.width * 1.2; } else { const sys = common_vendor.index.getSystemInfoSync(); this.canvasWidth = sys.windowWidth - 30; this.canvasHeight = this.canvasWidth * 1.2; } if (!this.ctx) { this.ctx = common_vendor.index.createCanvasContext("walker", this); this.lastFrameTime = Date.now(); this.ensureRenderLoop(); } }).exec(); }, 100); }, // Expose a method to be called from parent when a beat hits triggerBeat() { const coreRadius = Math.min(this.canvasWidth, this.canvasHeight) * 0.32; this.shockwaves.push({ radius: coreRadius, maxRadius: coreRadius * 2.2, opacity: 1 }); this.waveAmplitudeMultiplier = 2.5; this.coreVelocity -= 0.05; this.ensureRenderLoop(); }, onTouchStart(e) { const touch = e.touches[0]; if (!touch) return; const cx = this.canvasWidth / 2; const cy = this.canvasHeight / 2 - 30; const coreRadius = Math.min(this.canvasWidth, this.canvasHeight) * 0.32; const dx = touch.x - cx; const dy = touch.y - cy; const dist = Math.sqrt(dx * dx + dy * dy); if (dist <= coreRadius + 40) { this.pressed = true; this.coreTargetScale = 0.9; this.ensureRenderLoop(); } }, onTouchMove(e) { if (!this.pressed) return; const touch = e.touches[0]; if (!touch) return; const cx = this.canvasWidth / 2; const cy = this.canvasHeight / 2 - 30; const coreRadius = Math.min(this.canvasWidth, this.canvasHeight) * 0.32; const dx = touch.x - cx; const dy = touch.y - cy; const dist = Math.sqrt(dx * dx + dy * dy); if (dist > coreRadius + 40) { this.pressed = false; this.coreTargetScale = 1; } }, onTouchEnd() { this.coreTargetScale = 1; if (this.pressed) { this.$emit("toggle-play"); } this.pressed = false; }, onTouchCancel() { this.coreTargetScale = 1; this.pressed = false; }, ensureRenderLoop() { if (this.renderRunning) return; this.renderRunning = true; this.renderLoop(); }, renderLoop() { const fixedDt = 0.016; if (this.isPlaying) { this.time += fixedDt; } else { this.time += fixedDt * 0.2; } const tension = 120; const friction = 12; const force = (this.coreTargetScale - this.coreScale) * tension; this.coreVelocity += force * fixedDt; this.coreVelocity *= Math.exp(-friction * fixedDt); this.coreScale += this.coreVelocity * fixedDt; if (this.waveAmplitudeMultiplier > 1) { this.waveAmplitudeMultiplier -= fixedDt * 3; if (this.waveAmplitudeMultiplier < 1) this.waveAmplitudeMultiplier = 1; } else if (!this.isPlaying && this.waveAmplitudeMultiplier > 0.1) { this.waveAmplitudeMultiplier -= fixedDt * 2; if (this.waveAmplitudeMultiplier < 0.1) this.waveAmplitudeMultiplier = 0.1; } this.draw(); this.ctx.draw(false); const shouldContinue = this.isPlaying || this.shockwaves.length > 0 || Math.abs(this.coreScale - this.coreTargetScale) >= 1e-3 || Math.abs(this.coreVelocity) >= 1e-3 || this.waveAmplitudeMultiplier > 0.11; if (shouldContinue) { this.timer = setTimeout(() => { this.renderLoop(); }, 1e3 / 60); } else { this.timer = null; this.renderRunning = false; } }, draw() { const ctx = this.ctx; const W = this.canvasWidth; const H = this.canvasHeight; ctx.clearRect(0, 0, W, H); const coreRadius = Math.min(W, H) * 0.32; const cx = W / 2; const cy = H / 2 - 30; for (let i = this.shockwaves.length - 1; i >= 0; i--) { const sw = this.shockwaves[i]; sw.radius += (sw.maxRadius - sw.radius) * 0.08; sw.opacity -= 0.03; if (sw.opacity <= 0) { this.shockwaves.splice(i, 1); continue; } ctx.beginPath(); ctx.arc(cx, cy, sw.radius, 0, Math.PI * 2); ctx.strokeStyle = `rgba(16, 185, 129, ${sw.opacity * 0.5})`; ctx.lineWidth = 3; ctx.stroke(); } this.drawWaves(ctx, W, H); ctx.save(); ctx.translate(cx, cy); ctx.scale(this.coreScale, this.coreScale); ctx.setShadow(0, 12, 24, "rgba(16, 185, 129, 0.3)"); const coreGrad = ctx.createLinearGradient(-coreRadius, -coreRadius, coreRadius, coreRadius); coreGrad.addColorStop(0, "#34d399"); coreGrad.addColorStop(1, "#047857"); ctx.beginPath(); ctx.arc(0, 0, coreRadius, 0, Math.PI * 2); ctx.fillStyle = coreGrad; ctx.fill(); ctx.setShadow(0, 0, 0, "transparent"); ctx.beginPath(); ctx.arc(0, 0, coreRadius - 2, 0, Math.PI * 2); ctx.strokeStyle = "rgba(255, 255, 255, 0.35)"; ctx.lineWidth = 2; ctx.stroke(); const fontSize = Math.floor(coreRadius * 0.7); ctx.fillStyle = "#ffffff"; ctx.font = `bold ${fontSize}px "DIN Condensed", "Inter", sans-serif`; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(this.bpm.toString(), 0, -12); ctx.fillStyle = "rgba(255, 255, 255, 0.85)"; ctx.font = '500 16px "Inter", sans-serif'; ctx.fillText("BPM", 0, coreRadius * 0.3); ctx.fillStyle = "#ffffff"; const iconY = coreRadius * 0.55; if (this.isPlaying) { ctx.fillRect(-8, iconY - 6, 5, 14); ctx.fillRect(3, iconY - 6, 5, 14); } else { ctx.beginPath(); ctx.moveTo(-5, iconY - 8); ctx.lineTo(9, iconY); ctx.lineTo(-5, iconY + 8); ctx.closePath(); ctx.fill(); } ctx.restore(); }, drawWaves(ctx, W, H) { const baseY = H * 0.82; const dynamicAmp = H * 0.08; const waves = [ { color: "rgba(52, 211, 153, 0.3)", speed: 1.5, freq: 0.012, amp: dynamicAmp * 0.7, offset: 0 }, { color: "rgba(16, 185, 129, 0.6)", speed: 2.5, freq: 0.015, amp: dynamicAmp, offset: Math.PI } ]; const activeAmp = this.waveAmplitudeMultiplier; const t = this.time; waves.forEach((wave) => { ctx.beginPath(); ctx.moveTo(0, H); ctx.lineTo(0, baseY); const step = 25; for (let x = 0; x <= W; x += step) { const y = baseY + Math.sin(x * wave.freq + t * wave.speed + wave.offset) * wave.amp * activeAmp; ctx.lineTo(x, y); } const finalY = baseY + Math.sin(W * wave.freq + t * wave.speed + wave.offset) * wave.amp * activeAmp; ctx.lineTo(W, finalY); ctx.lineTo(W, H); ctx.closePath(); ctx.fillStyle = wave.color; ctx.fill(); }); } } }; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { return { a: $data.canvasWidth + "px", b: $data.canvasHeight + "px", c: common_vendor.o((...args) => $options.onTouchStart && $options.onTouchStart(...args), "a1"), d: common_vendor.o((...args) => $options.onTouchMove && $options.onTouchMove(...args), "c5"), e: common_vendor.o((...args) => $options.onTouchEnd && $options.onTouchEnd(...args), "fe"), f: common_vendor.o((...args) => $options.onTouchCancel && $options.onTouchCancel(...args), "2c") }; } const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-782dd882"]]); wx.createComponent(Component); //# sourceMappingURL=../../../../.sourcemap/mp-weixin/training/pages/components/walker-canvas.js.map