gengx
This commit is contained in:
@@ -1,238 +1,2 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __publicField = (obj, key, value) => {
|
||||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
return value;
|
||||
};
|
||||
const common_vendor = require("../../common/vendor.js");
|
||||
class AudioPool {
|
||||
constructor(src, size, volume = 1) {
|
||||
__publicField(this, "list", []);
|
||||
__publicField(this, "cursor", 0);
|
||||
__publicField(this, "warmedUp", false);
|
||||
__publicField(this, "targetVolume", 1);
|
||||
this.src = src;
|
||||
this.size = size;
|
||||
this.targetVolume = volume;
|
||||
}
|
||||
create() {
|
||||
for (let i = 0; i < this.size; i++) {
|
||||
const ctx = common_vendor.index.createInnerAudioContext();
|
||||
ctx.src = this.src;
|
||||
ctx.obeyMuteSwitch = false;
|
||||
ctx.autoplay = false;
|
||||
ctx.volume = this.targetVolume;
|
||||
this.list.push(ctx);
|
||||
}
|
||||
}
|
||||
setVolume(volume) {
|
||||
this.targetVolume = Math.max(0, Math.min(1, volume));
|
||||
this.list.forEach((ctx) => {
|
||||
try {
|
||||
ctx.volume = this.targetVolume;
|
||||
} catch (_) {
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 预热:短暂播放,让音频文件下载/解码到内存
|
||||
* 真正首次 play 不会再卡冷启动
|
||||
*/
|
||||
warmUp() {
|
||||
if (this.warmedUp)
|
||||
return;
|
||||
if (this.list.length === 0)
|
||||
this.create();
|
||||
this.list.forEach((ctx) => {
|
||||
try {
|
||||
ctx.volume = 0;
|
||||
ctx.play();
|
||||
setTimeout(() => {
|
||||
try {
|
||||
ctx.stop();
|
||||
ctx.volume = this.targetVolume;
|
||||
} catch (_) {
|
||||
}
|
||||
}, 60);
|
||||
} catch (_) {
|
||||
}
|
||||
});
|
||||
this.warmedUp = true;
|
||||
}
|
||||
play() {
|
||||
if (this.list.length === 0) {
|
||||
this.create();
|
||||
this.warmUp();
|
||||
}
|
||||
const ctx = this.list[this.cursor];
|
||||
this.cursor = (this.cursor + 1) % this.list.length;
|
||||
try {
|
||||
this.list.forEach((c) => {
|
||||
try {
|
||||
c.stop();
|
||||
} catch (_) {
|
||||
}
|
||||
});
|
||||
ctx.play();
|
||||
} catch (_) {
|
||||
try {
|
||||
ctx.play();
|
||||
} catch (__) {
|
||||
}
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
this.list.forEach((ctx) => {
|
||||
var _a;
|
||||
try {
|
||||
(_a = ctx.destroy) == null ? void 0 : _a.call(ctx);
|
||||
} catch (_) {
|
||||
}
|
||||
});
|
||||
this.list = [];
|
||||
this.warmedUp = false;
|
||||
}
|
||||
}
|
||||
const DEFAULT_CLICK_SRC = "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260526/20260526105128557ef8669.mp3";
|
||||
function useMetronome(options = {}) {
|
||||
const {
|
||||
initialBpm = 80,
|
||||
bpmMin = 40,
|
||||
bpmMax = 240,
|
||||
clickSrc = DEFAULT_CLICK_SRC,
|
||||
accentSrc,
|
||||
poolSize = 4,
|
||||
volume = 1,
|
||||
silent = false,
|
||||
onBeat
|
||||
} = options;
|
||||
const bpm = common_vendor.ref(initialBpm);
|
||||
const isPlaying = common_vendor.ref(false);
|
||||
const beatIndex = common_vendor.ref(0);
|
||||
const isAccent = common_vendor.ref(false);
|
||||
const accentEveryRef = common_vendor.ref(options.accentEvery ?? 4);
|
||||
const intervalMs = common_vendor.computed(() => 6e4 / bpm.value);
|
||||
let clickPool = null;
|
||||
let accentPool = null;
|
||||
let timer = null;
|
||||
let nextTickAt = 0;
|
||||
const ensureAudio = () => {
|
||||
if (silent)
|
||||
return;
|
||||
if (!clickPool) {
|
||||
clickPool = new AudioPool(clickSrc, poolSize, volume);
|
||||
clickPool.warmUp();
|
||||
}
|
||||
if (accentSrc && !accentPool) {
|
||||
accentPool = new AudioPool(accentSrc, Math.max(2, Math.ceil(poolSize / 2)), volume);
|
||||
accentPool.warmUp();
|
||||
}
|
||||
};
|
||||
const playSound = (accent) => {
|
||||
if (accent && accentPool) {
|
||||
accentPool.play();
|
||||
} else if (clickPool) {
|
||||
clickPool.play();
|
||||
}
|
||||
};
|
||||
const tick = () => {
|
||||
if (!isPlaying.value)
|
||||
return;
|
||||
const every = Math.max(1, accentEveryRef.value);
|
||||
const accent = beatIndex.value % every === 0;
|
||||
isAccent.value = accent;
|
||||
playSound(accent);
|
||||
onBeat == null ? void 0 : onBeat(beatIndex.value, accent);
|
||||
beatIndex.value++;
|
||||
nextTickAt += intervalMs.value;
|
||||
const nextDelay = Math.max(0, nextTickAt - Date.now());
|
||||
timer = setTimeout(tick, nextDelay);
|
||||
};
|
||||
const start = () => {
|
||||
if (isPlaying.value)
|
||||
return;
|
||||
ensureAudio();
|
||||
isPlaying.value = true;
|
||||
beatIndex.value = 0;
|
||||
nextTickAt = Date.now();
|
||||
tick();
|
||||
};
|
||||
const stop = () => {
|
||||
isPlaying.value = false;
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
const setBpm = (val) => {
|
||||
bpm.value = Math.max(bpmMin, Math.min(bpmMax, Math.round(val)));
|
||||
};
|
||||
const setAccentEvery = (n) => {
|
||||
accentEveryRef.value = Math.max(1, Math.min(16, Math.round(n)));
|
||||
beatIndex.value = 0;
|
||||
};
|
||||
const updateAudioSrc = (newClickSrc, newAccentSrc) => {
|
||||
const wasPlaying = isPlaying.value;
|
||||
if (wasPlaying)
|
||||
stop();
|
||||
clickPool == null ? void 0 : clickPool.destroy();
|
||||
accentPool == null ? void 0 : accentPool.destroy();
|
||||
clickPool = null;
|
||||
accentPool = null;
|
||||
clickPool = new AudioPool(newClickSrc, poolSize);
|
||||
if (newAccentSrc) {
|
||||
accentPool = new AudioPool(newAccentSrc, Math.max(2, Math.ceil(poolSize / 2)));
|
||||
}
|
||||
const previewCtx = common_vendor.index.createInnerAudioContext();
|
||||
previewCtx.src = newClickSrc;
|
||||
previewCtx.obeyMuteSwitch = false;
|
||||
previewCtx.volume = 0.4;
|
||||
try {
|
||||
previewCtx.play();
|
||||
setTimeout(() => {
|
||||
var _a;
|
||||
try {
|
||||
(_a = previewCtx.destroy) == null ? void 0 : _a.call(previewCtx);
|
||||
} catch (_) {
|
||||
}
|
||||
}, 500);
|
||||
} catch (_) {
|
||||
}
|
||||
if (wasPlaying)
|
||||
start();
|
||||
};
|
||||
const preload = () => {
|
||||
try {
|
||||
common_vendor.index.setInnerAudioOption({
|
||||
obeyMuteSwitch: false,
|
||||
mixWithOther: true
|
||||
});
|
||||
} catch (_) {
|
||||
}
|
||||
ensureAudio();
|
||||
};
|
||||
common_vendor.onUnmounted(() => {
|
||||
stop();
|
||||
clickPool == null ? void 0 : clickPool.destroy();
|
||||
accentPool == null ? void 0 : accentPool.destroy();
|
||||
clickPool = null;
|
||||
accentPool = null;
|
||||
});
|
||||
return {
|
||||
bpm,
|
||||
isPlaying,
|
||||
beatIndex,
|
||||
isAccent,
|
||||
intervalMs,
|
||||
accentEvery: accentEveryRef,
|
||||
start,
|
||||
stop,
|
||||
setBpm,
|
||||
setAccentEvery,
|
||||
updateAudioSrc,
|
||||
preload
|
||||
};
|
||||
}
|
||||
exports.useMetronome = useMetronome;
|
||||
"use strict";var R=Object.defineProperty;var B=(l,t,e)=>t in l?R(l,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):l[t]=e;var m=(l,t,e)=>(B(l,typeof t!="symbol"?t+"":t,e),e);const i=require("../../common/vendor.js");class d{constructor(t,e,o=1){m(this,"list",[]);m(this,"cursor",0);m(this,"warmedUp",!1);m(this,"targetVolume",1);this.src=t,this.size=e,this.targetVolume=o}create(){for(let t=0;t<this.size;t++){const e=i.index.createInnerAudioContext();e.src=this.src,e.obeyMuteSwitch=!1,e.autoplay=!1,e.volume=this.targetVolume,this.list.push(e)}}setVolume(t){this.targetVolume=Math.max(0,Math.min(1,t)),this.list.forEach(e=>{try{e.volume=this.targetVolume}catch{}})}warmUp(){this.warmedUp||(this.list.length===0&&this.create(),this.list.forEach(t=>{try{t.volume=0,t.play(),setTimeout(()=>{try{t.stop(),t.volume=this.targetVolume}catch{}},60)}catch{}}),this.warmedUp=!0)}play(){this.list.length===0&&(this.create(),this.warmUp());const t=this.list[this.cursor];this.cursor=(this.cursor+1)%this.list.length;try{this.list.forEach(e=>{try{e.stop()}catch{}}),t.play()}catch{try{t.play()}catch{}}}destroy(){this.list.forEach(t=>{var e;try{(e=t.destroy)==null||e.call(t)}catch{}}),this.list=[],this.warmedUp=!1}}const F="https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260526/20260526105128557ef8669.mp3";function K(l={}){const{initialBpm:t=80,bpmMin:e=40,bpmMax:o=240,clickSrc:z=F,accentSrc:U,poolSize:f=4,volume:A=1,silent:I=!1,onBeat:v}=l,M=i.ref(t),u=i.ref(!1),c=i.ref(0),E=i.ref(!1),w=i.ref(l.accentEvery??4),b=i.computed(()=>6e4/M.value);let s=null,a=null,y=null,g=0;const S=()=>{I||(s||(s=new d(z,f,A),s.warmUp()),U&&!a&&(a=new d(U,Math.max(2,Math.ceil(f/2)),A),a.warmUp()))},D=r=>{r&&a?a.play():s&&s.play()},V=()=>{if(!u.value)return;const r=Math.max(1,w.value),h=c.value%r===0;E.value=h,D(h),v==null||v(c.value,h),c.value++,g+=b.value;const p=Math.max(0,g-Date.now());y=setTimeout(V,p)},T=()=>{u.value||(S(),u.value=!0,c.value=0,g=Date.now(),V())},_=()=>{u.value=!1,y&&(clearTimeout(y),y=null)},C=r=>{M.value=Math.max(e,Math.min(o,Math.round(r)))},q=r=>{w.value=Math.max(1,Math.min(16,Math.round(r))),c.value=0},L=(r,h)=>{const p=u.value;p&&_(),s==null||s.destroy(),a==null||a.destroy(),s=null,a=null,s=new d(r,f),h&&(a=new d(h,Math.max(2,Math.ceil(f/2))));const n=i.index.createInnerAudioContext();n.src=r,n.obeyMuteSwitch=!1,n.volume=.4;try{n.play(),setTimeout(()=>{var x;try{(x=n.destroy)==null||x.call(n)}catch{}},500)}catch{}p&&T()},O=()=>{try{i.index.setInnerAudioOption({obeyMuteSwitch:!1,mixWithOther:!0})}catch{}S()};return i.onUnmounted(()=>{_(),s==null||s.destroy(),a==null||a.destroy(),s=null,a=null}),{bpm:M,isPlaying:u,beatIndex:c,isAccent:E,intervalMs:b,accentEvery:w,start:T,stop:_,setBpm:C,setAccentEvery:q,updateAudioSrc:L,preload:O}}exports.useMetronome=K;
|
||||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/training/hooks/useMetronome.js.map
|
||||
|
||||
+1
-465
File diff suppressed because one or more lines are too long
+1
-15
@@ -1,15 +1 @@
|
||||
.crush-canvas-container.data-v-e11c993f {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
.crush-canvas.data-v-e11c993f {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
.crush-canvas-container.data-v-e11c993f{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:10}.crush-canvas.data-v-e11c993f{display:block;width:100%;height:100%;background:transparent}
|
||||
+1
-277
File diff suppressed because one or more lines are too long
+1
-15
@@ -1,15 +1 @@
|
||||
.walker-canvas-container.data-v-782dd882 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.walker-canvas.data-v-782dd882 {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent; /* Pure transparent to blend into page */
|
||||
}
|
||||
.walker-canvas-container.data-v-782dd882{width:100%;height:100%;position:absolute;top:0;left:0;right:0;bottom:0}.walker-canvas.data-v-782dd882{display:block;width:100%;height:100%;background:transparent}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1079
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,150 +1,2 @@
|
||||
"use strict";
|
||||
const common_vendor = require("../../common/vendor.js");
|
||||
const training_hooks_useMetronome = require("../hooks/useMetronome.js");
|
||||
if (!Math) {
|
||||
WalkerCanvas();
|
||||
}
|
||||
const WalkerCanvas = () => "./components/walker-canvas.js";
|
||||
const _sfc_main = /* @__PURE__ */ common_vendor.defineComponent({
|
||||
__name: "metronome",
|
||||
setup(__props) {
|
||||
const LOOP_PRESETS = [
|
||||
{ id: "slow", bpm: 110, label: "慢走", desc: "热身 · 恢复" },
|
||||
{ id: "normal", bpm: 130, label: "健走", desc: "日常 · 通勤" },
|
||||
{ id: "brisk", bpm: 150, label: "快走", desc: "提速 · 燃脂" }
|
||||
];
|
||||
const customExpanded = common_vendor.ref(false);
|
||||
const currentLoop = common_vendor.ref("normal");
|
||||
const currentSound = common_vendor.ref("crisp");
|
||||
const walkerCanvasRef = common_vendor.ref(null);
|
||||
const SOUND_PRESETS = {
|
||||
crisp: {
|
||||
normal: "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260527/202605271539371ebe13672.mp3",
|
||||
accent: "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260527/202605271539376ff628472.mp3"
|
||||
},
|
||||
wood: {
|
||||
normal: "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260526/202605261051282a0a94508.mp3",
|
||||
accent: "https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260526/202605261051282a0a94508.mp3"
|
||||
}
|
||||
};
|
||||
const fg = training_hooks_useMetronome.useMetronome({
|
||||
initialBpm: 130,
|
||||
accentEvery: 2,
|
||||
clickSrc: SOUND_PRESETS.crisp.normal,
|
||||
accentSrc: SOUND_PRESETS.crisp.accent,
|
||||
poolSize: 4,
|
||||
// 【完美音画同步核心】
|
||||
onBeat: (index, isAccent) => {
|
||||
if (walkerCanvasRef.value && walkerCanvasRef.value.triggerBeat) {
|
||||
walkerCanvasRef.value.triggerBeat();
|
||||
}
|
||||
}
|
||||
});
|
||||
const isPlaying = common_vendor.computed(() => fg.isPlaying.value);
|
||||
const currentBpm = common_vendor.computed(() => fg.bpm.value);
|
||||
const customAccent = common_vendor.computed(() => fg.accentEvery.value);
|
||||
const intervalMs = common_vendor.computed(() => 6e4 / fg.bpm.value);
|
||||
const onCenterTap = () => {
|
||||
if (fg.isPlaying.value) {
|
||||
fg.stop();
|
||||
common_vendor.index.setKeepScreenOn({ keepScreenOn: false });
|
||||
} else {
|
||||
fg.start();
|
||||
common_vendor.index.setKeepScreenOn({ keepScreenOn: true });
|
||||
}
|
||||
};
|
||||
const onPresetTap = (id) => {
|
||||
const preset = LOOP_PRESETS.find((p) => p.id === id);
|
||||
if (!preset)
|
||||
return;
|
||||
currentLoop.value = id;
|
||||
fg.setBpm(preset.bpm);
|
||||
if (!fg.isPlaying.value) {
|
||||
fg.start();
|
||||
common_vendor.index.setKeepScreenOn({ keepScreenOn: true });
|
||||
}
|
||||
};
|
||||
const onToggleCustom = () => {
|
||||
customExpanded.value = !customExpanded.value;
|
||||
if (customExpanded.value) {
|
||||
fg.preload();
|
||||
} else {
|
||||
const matched = LOOP_PRESETS.find((p) => p.bpm === fg.bpm.value);
|
||||
currentLoop.value = matched ? matched.id : null;
|
||||
}
|
||||
};
|
||||
const onBpmDelta = (delta) => {
|
||||
fg.setBpm(fg.bpm.value + delta);
|
||||
currentLoop.value = null;
|
||||
};
|
||||
const onAccentSelect = (n) => {
|
||||
fg.setAccentEvery(n);
|
||||
};
|
||||
const onSoundSelect = (sound) => {
|
||||
currentSound.value = sound;
|
||||
const preset = SOUND_PRESETS[sound];
|
||||
fg.updateAudioSrc(preset.normal, preset.accent);
|
||||
};
|
||||
const rootStyle = common_vendor.computed(() => ({
|
||||
"--beat-duration": `${intervalMs.value}ms`
|
||||
}));
|
||||
common_vendor.onShow(() => {
|
||||
fg.preload();
|
||||
});
|
||||
function cleanupMetronome() {
|
||||
fg.stop();
|
||||
common_vendor.index.setKeepScreenOn({ keepScreenOn: false });
|
||||
}
|
||||
common_vendor.onHide(cleanupMetronome);
|
||||
common_vendor.onUnload(cleanupMetronome);
|
||||
common_vendor.onUnmounted(cleanupMetronome);
|
||||
return (_ctx, _cache) => {
|
||||
return common_vendor.e({
|
||||
a: common_vendor.sr(walkerCanvasRef, "a75aa31b-0", {
|
||||
"k": "walkerCanvasRef"
|
||||
}),
|
||||
b: common_vendor.o(onCenterTap, "0f"),
|
||||
c: common_vendor.p({
|
||||
bpm: currentBpm.value,
|
||||
["is-playing"]: isPlaying.value
|
||||
}),
|
||||
d: currentSound.value === "crisp" ? 1 : "",
|
||||
e: common_vendor.o(($event) => onSoundSelect("crisp"), "8c"),
|
||||
f: currentSound.value === "wood" ? 1 : "",
|
||||
g: common_vendor.o(($event) => onSoundSelect("wood"), "43"),
|
||||
h: common_vendor.f(LOOP_PRESETS, (p, k0, i0) => {
|
||||
return {
|
||||
a: common_vendor.t(p.label),
|
||||
b: common_vendor.t(p.bpm),
|
||||
c: common_vendor.t(p.desc),
|
||||
d: p.id,
|
||||
e: currentLoop.value === p.id ? 1 : "",
|
||||
f: common_vendor.o(($event) => onPresetTap(p.id), p.id)
|
||||
};
|
||||
}),
|
||||
i: common_vendor.t(customExpanded.value ? "✕ 收起自定义" : "⚙ 自定义节奏"),
|
||||
j: common_vendor.o(onToggleCustom, "29"),
|
||||
k: customExpanded.value
|
||||
}, customExpanded.value ? {
|
||||
l: common_vendor.o(($event) => onBpmDelta(-5), "71"),
|
||||
m: common_vendor.o(($event) => onBpmDelta(-1), "5b"),
|
||||
n: common_vendor.t(currentBpm.value),
|
||||
o: common_vendor.o(($event) => onBpmDelta(1), "90"),
|
||||
p: common_vendor.o(($event) => onBpmDelta(5), "b0"),
|
||||
q: common_vendor.f([2, 3, 4], (n, k0, i0) => {
|
||||
return {
|
||||
a: common_vendor.t(n),
|
||||
b: n,
|
||||
c: customAccent.value === n ? 1 : "",
|
||||
d: common_vendor.o(($event) => onAccentSelect(n), n)
|
||||
};
|
||||
})
|
||||
} : {}, {
|
||||
r: common_vendor.s(rootStyle.value)
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-a75aa31b"]]);
|
||||
wx.createPage(MiniProgramPage);
|
||||
"use strict";const e=require("../../common/vendor.js"),x=require("../hooks/useMetronome.js");Math||z();const z=()=>"./components/walker-canvas.js",$=e.defineComponent({__name:"metronome",setup(E){const u=[{id:"slow",bpm:110,label:"慢走",desc:"热身 · 恢复"},{id:"normal",bpm:130,label:"健走",desc:"日常 · 通勤"},{id:"brisk",bpm:150,label:"快走",desc:"提速 · 燃脂"}],c=e.ref(!1),s=e.ref("normal"),p=e.ref("crisp"),l=e.ref(null),i={crisp:{normal:"https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260527/202605271539371ebe13672.mp3",accent:"https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260527/202605271539376ff628472.mp3"},wood:{normal:"https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260526/202605261051282a0a94508.mp3",accent:"https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260526/202605261051282a0a94508.mp3"}},o=x.useMetronome({initialBpm:130,accentEvery:2,clickSrc:i.crisp.normal,accentSrc:i.crisp.accent,poolSize:4,onBeat:(t,a)=>{l.value&&l.value.triggerBeat&&l.value.triggerBeat()}}),f=e.computed(()=>o.isPlaying.value),d=e.computed(()=>o.bpm.value),g=e.computed(()=>o.accentEvery.value),b=e.computed(()=>6e4/o.bpm.value),S=()=>{o.isPlaying.value?(o.stop(),e.index.setKeepScreenOn({keepScreenOn:!1})):(o.start(),e.index.setKeepScreenOn({keepScreenOn:!0}))},_=t=>{const a=u.find(n=>n.id===t);a&&(s.value=t,o.setBpm(a.bpm),o.isPlaying.value||(o.start(),e.index.setKeepScreenOn({keepScreenOn:!0})))},k=()=>{if(c.value=!c.value,c.value)o.preload();else{const t=u.find(a=>a.bpm===o.bpm.value);s.value=t?t.id:null}},r=t=>{o.setBpm(o.bpm.value+t),s.value=null},h=t=>{o.setAccentEvery(t)},v=t=>{p.value=t;const a=i[t];o.updateAudioSrc(a.normal,a.accent)},y=e.computed(()=>({"--beat-duration":`${b.value}ms`}));e.onShow(()=>{o.preload()});function m(){o.stop(),e.index.setKeepScreenOn({keepScreenOn:!1})}return e.onHide(m),e.onUnload(m),e.onUnmounted(m),(t,a)=>e.e({a:e.sr(l,"a75aa31b-0",{k:"walkerCanvasRef"}),b:e.o(S,"0f"),c:e.p({bpm:d.value,"is-playing":f.value}),d:p.value==="crisp"?1:"",e:e.o(n=>v("crisp"),"8c"),f:p.value==="wood"?1:"",g:e.o(n=>v("wood"),"43"),h:e.f(u,(n,O,P)=>({a:e.t(n.label),b:e.t(n.bpm),c:e.t(n.desc),d:n.id,e:s.value===n.id?1:"",f:e.o(w=>_(n.id),n.id)})),i:e.t(c.value?"✕ 收起自定义":"⚙ 自定义节奏"),j:e.o(k,"29"),k:c.value},c.value?{l:e.o(n=>r(-5),"71"),m:e.o(n=>r(-1),"5b"),n:e.t(d.value),o:e.o(n=>r(1),"90"),p:e.o(n=>r(5),"b0"),q:e.f([2,3,4],(n,O,P)=>({a:e.t(n),b:n,c:g.value===n?1:"",d:e.o(w=>h(n),n)}))}:{},{r:e.s(y.value)})}}),B=e._export_sfc($,[["__scopeId","data-v-a75aa31b"]]);wx.createPage(B);
|
||||
//# sourceMappingURL=../../../.sourcemap/mp-weixin/training/pages/metronome.js.map
|
||||
|
||||
@@ -1,311 +1 @@
|
||||
/**
|
||||
* 这里是uni-app内置的常用样式变量
|
||||
*
|
||||
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
|
||||
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
|
||||
*
|
||||
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
|
||||
*/
|
||||
/* 颜色变量 */
|
||||
/* 行为相关颜色 */
|
||||
/* 文字基本颜色 */
|
||||
/* 背景颜色 */
|
||||
/* 边框颜色 */
|
||||
/* 尺寸变量 */
|
||||
/* 文字尺寸 */
|
||||
/* 图片尺寸 */
|
||||
/* Border Radius */
|
||||
/* 水平间距 */
|
||||
/* 垂直间距 */
|
||||
/* 透明度 */
|
||||
/* 文章场景相关 */
|
||||
/* ============================================================
|
||||
* 设计 token —— 浅色清爽
|
||||
* ============================================================ */
|
||||
/* ============================================================
|
||||
* 页面容器
|
||||
* ============================================================ */
|
||||
.page.data-v-a75aa31b {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
padding: 30rpx 28rpx 50rpx;
|
||||
background: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 统一运动律动视区 (Canvas)
|
||||
* ============================================================ */
|
||||
.stage-canvas-wrapper.data-v-a75aa31b {
|
||||
width: 100vw;
|
||||
margin-left: -28rpx;
|
||||
margin-right: -28rpx;
|
||||
flex-shrink: 0;
|
||||
/* 防止被压缩 */
|
||||
height: 600rpx;
|
||||
/* 固定高度,不随内容变化 */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 音色选择器
|
||||
* ============================================================ */
|
||||
.sound-selector.data-v-a75aa31b {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 14rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.sound-option.data-v-a75aa31b {
|
||||
flex: 1;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid rgba(15, 23, 42, 0.06);
|
||||
border-radius: 24rpx;
|
||||
padding: 20rpx 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
transition: all 0.18s;
|
||||
}
|
||||
.sound-option .sound-icon.data-v-a75aa31b {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
.sound-option .sound-label.data-v-a75aa31b {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
.sound-option.data-v-a75aa31b:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
.sound-option.active.data-v-a75aa31b {
|
||||
background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
|
||||
border-color: #10b981;
|
||||
box-shadow: 0 8rpx 20rpx rgba(16, 185, 129, 0.18), inset 0 0 0 2rpx rgba(16, 185, 129, 0.4);
|
||||
}
|
||||
.sound-option.active .sound-label.data-v-a75aa31b {
|
||||
color: #047857;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 三档位推荐
|
||||
* ============================================================ */
|
||||
.presets.data-v-a75aa31b {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 14rpx;
|
||||
}
|
||||
.preset.data-v-a75aa31b {
|
||||
flex: 1;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid rgba(15, 23, 42, 0.06);
|
||||
border-radius: 24rpx;
|
||||
padding: 26rpx 8rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
transition: all 0.18s;
|
||||
}
|
||||
.preset .preset-name.data-v-a75aa31b {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
.preset .preset-bpm.data-v-a75aa31b {
|
||||
font-size: 22rpx;
|
||||
color: #475569;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.preset .preset-desc.data-v-a75aa31b {
|
||||
font-size: 18rpx;
|
||||
color: #94a3b8;
|
||||
letter-spacing: 1rpx;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
.preset.data-v-a75aa31b:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
.preset.active.data-v-a75aa31b {
|
||||
background: #d1fae5;
|
||||
border-color: #10b981;
|
||||
box-shadow: 0 8rpx 20rpx rgba(16, 185, 129, 0.18), inset 0 0 0 2rpx rgba(16, 185, 129, 0.4);
|
||||
}
|
||||
.preset.active .preset-name.data-v-a75aa31b {
|
||||
color: #047857;
|
||||
}
|
||||
.preset.active .preset-bpm.data-v-a75aa31b {
|
||||
color: #047857;
|
||||
}
|
||||
.preset.active .preset-desc.data-v-a75aa31b {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 自定义节奏区域(标题栏 + 折叠内容)
|
||||
* ============================================================ */
|
||||
.custom-section.data-v-a75aa31b {
|
||||
width: 100%;
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
.custom-header.data-v-a75aa31b {
|
||||
width: 100%;
|
||||
padding: 16rpx 6rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.custom-header .custom-title.data-v-a75aa31b {
|
||||
font-size: 22rpx;
|
||||
color: #94a3b8;
|
||||
letter-spacing: 0.5rpx;
|
||||
}
|
||||
.custom-header .custom-title.data-v-a75aa31b:active {
|
||||
color: #475569;
|
||||
}
|
||||
.custom-content.data-v-a75aa31b {
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid rgba(245, 158, 11, 0.25);
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 6rpx 18rpx rgba(245, 158, 11, 0.08);
|
||||
padding: 20rpx 24rpx 22rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
.custom-warn.data-v-a75aa31b {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
.custom-warn .warn-dot.data-v-a75aa31b {
|
||||
font-size: 18rpx;
|
||||
color: #f59e0b;
|
||||
}
|
||||
.custom-warn .warn-text.data-v-a75aa31b {
|
||||
font-size: 22rpx;
|
||||
color: #b45309;
|
||||
letter-spacing: 0.5rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
.row.data-v-a75aa31b {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12rpx;
|
||||
}
|
||||
.row .row-label.data-v-a75aa31b {
|
||||
font-size: 24rpx;
|
||||
color: #475569;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1rpx;
|
||||
flex-shrink: 0;
|
||||
width: 70rpx;
|
||||
}
|
||||
.bpm-stepper.data-v-a75aa31b {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.bpm-stepper .step-btn.data-v-a75aa31b {
|
||||
min-width: 56rpx;
|
||||
height: 52rpx;
|
||||
line-height: 52rpx;
|
||||
text-align: center;
|
||||
font-size: 22rpx;
|
||||
color: #475569;
|
||||
background: #f1f5f9;
|
||||
border-radius: 10rpx;
|
||||
font-weight: 600;
|
||||
padding: 0 10rpx;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.bpm-stepper .step-btn.data-v-a75aa31b:active {
|
||||
background: #e2e8f0;
|
||||
transform: scale(0.94);
|
||||
}
|
||||
.bpm-stepper .step-val.data-v-a75aa31b {
|
||||
min-width: 84rpx;
|
||||
height: 52rpx;
|
||||
line-height: 52rpx;
|
||||
text-align: center;
|
||||
font-size: 30rpx;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
font-variant-numeric: tabular-nums;
|
||||
background: #fef3c7;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
.meter-tabs.data-v-a75aa31b {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
}
|
||||
.meter-tabs .meter-tab.data-v-a75aa31b {
|
||||
height: 52rpx;
|
||||
line-height: 52rpx;
|
||||
padding: 0 18rpx;
|
||||
font-size: 22rpx;
|
||||
color: #475569;
|
||||
background: #f1f5f9;
|
||||
border-radius: 10rpx;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
.meter-tabs .meter-tab.active.data-v-a75aa31b {
|
||||
background: #f59e0b;
|
||||
color: #fff;
|
||||
box-shadow: 0 4rpx 10rpx rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
.meter-tabs .meter-tab.data-v-a75aa31b:active {
|
||||
transform: scale(0.94);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 底部行(已废弃,保留样式以防引用)
|
||||
* ============================================================ */
|
||||
.bottom-row.data-v-a75aa31b {
|
||||
width: 100%;
|
||||
margin-top: 6rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 6rpx;
|
||||
}
|
||||
.bottom-link.data-v-a75aa31b {
|
||||
font-size: 22rpx;
|
||||
color: #94a3b8;
|
||||
letter-spacing: 0.5rpx;
|
||||
padding: 8rpx 4rpx;
|
||||
}
|
||||
.bottom-link.data-v-a75aa31b:active {
|
||||
color: #475569;
|
||||
}
|
||||
.bottom-hint.data-v-a75aa31b {
|
||||
font-size: 22rpx;
|
||||
color: #94a3b8;
|
||||
letter-spacing: 0.5rpx;
|
||||
}
|
||||
.page.data-v-a75aa31b{position:relative;min-height:100vh;box-sizing:border-box;padding:30rpx 28rpx 50rpx;background:#f8fafc;display:flex;flex-direction:column;align-items:center;gap:20rpx;color:#0f172a}.stage-canvas-wrapper.data-v-a75aa31b{width:100vw;margin-left:-28rpx;margin-right:-28rpx;flex-shrink:0;height:600rpx;display:flex;justify-content:center;align-items:center;margin-bottom:24rpx;position:relative}.sound-selector.data-v-a75aa31b{width:100%;display:flex;gap:14rpx;margin-bottom:20rpx}.sound-option.data-v-a75aa31b{flex:1;background:#fff;border:2rpx solid rgba(15,23,42,.06);border-radius:24rpx;padding:20rpx 16rpx;display:flex;align-items:center;justify-content:center;gap:10rpx;box-shadow:0 4rpx 12rpx rgba(15,23,42,.04);transition:all .18s}.sound-option .sound-icon.data-v-a75aa31b{font-size:32rpx}.sound-option .sound-label.data-v-a75aa31b{font-size:26rpx;font-weight:600;color:#475569;letter-spacing:1rpx}.sound-option.data-v-a75aa31b:active{transform:scale(.97)}.sound-option.active.data-v-a75aa31b{background:linear-gradient(135deg,#d1fae5,#a7f3d0);border-color:#10b981;box-shadow:0 8rpx 20rpx rgba(16,185,129,.18),inset 0 0 0 2rpx rgba(16,185,129,.4)}.sound-option.active .sound-label.data-v-a75aa31b{color:#047857}.presets.data-v-a75aa31b{width:100%;display:flex;gap:14rpx}.preset.data-v-a75aa31b{flex:1;background:#fff;border:2rpx solid rgba(15,23,42,.06);border-radius:24rpx;padding:26rpx 8rpx;display:flex;flex-direction:column;align-items:center;gap:8rpx;box-shadow:0 4rpx 12rpx rgba(15,23,42,.04);transition:all .18s}.preset .preset-name.data-v-a75aa31b{font-size:28rpx;font-weight:700;color:#0f172a;letter-spacing:2rpx}.preset .preset-bpm.data-v-a75aa31b{font-size:22rpx;color:#475569;font-weight:600;font-variant-numeric:tabular-nums}.preset .preset-desc.data-v-a75aa31b{font-size:18rpx;color:#94a3b8;letter-spacing:1rpx;margin-top:2rpx}.preset.data-v-a75aa31b:active{transform:scale(.97)}.preset.active.data-v-a75aa31b{background:#d1fae5;border-color:#10b981;box-shadow:0 8rpx 20rpx rgba(16,185,129,.18),inset 0 0 0 2rpx rgba(16,185,129,.4)}.preset.active .preset-name.data-v-a75aa31b,.preset.active .preset-bpm.data-v-a75aa31b{color:#047857}.preset.active .preset-desc.data-v-a75aa31b{color:#10b981}.custom-section.data-v-a75aa31b{width:100%;margin-top:12rpx}.custom-header.data-v-a75aa31b{width:100%;padding:16rpx 6rpx;display:flex;align-items:center;justify-content:center}.custom-header .custom-title.data-v-a75aa31b{font-size:22rpx;color:#94a3b8;letter-spacing:.5rpx}.custom-header .custom-title.data-v-a75aa31b:active{color:#475569}.custom-content.data-v-a75aa31b{width:100%;background:#fff;border:2rpx solid rgba(245,158,11,.25);border-radius:24rpx;box-shadow:0 6rpx 18rpx rgba(245,158,11,.08);padding:20rpx 24rpx 22rpx;display:flex;flex-direction:column;gap:16rpx;margin-top:8rpx}.custom-warn.data-v-a75aa31b{display:flex;align-items:center;gap:8rpx;margin-bottom:4rpx}.custom-warn .warn-dot.data-v-a75aa31b{font-size:18rpx;color:#f59e0b}.custom-warn .warn-text.data-v-a75aa31b{font-size:22rpx;color:#b45309;letter-spacing:.5rpx;font-weight:600}.row.data-v-a75aa31b{display:flex;align-items:center;justify-content:space-between;gap:12rpx}.row .row-label.data-v-a75aa31b{font-size:24rpx;color:#475569;font-weight:600;letter-spacing:1rpx;flex-shrink:0;width:70rpx}.bpm-stepper.data-v-a75aa31b{display:flex;align-items:center;gap:6rpx;flex:1;justify-content:flex-end}.bpm-stepper .step-btn.data-v-a75aa31b{min-width:56rpx;height:52rpx;line-height:52rpx;text-align:center;font-size:22rpx;color:#475569;background:#f1f5f9;border-radius:10rpx;font-weight:600;padding:0 10rpx;font-variant-numeric:tabular-nums}.bpm-stepper .step-btn.data-v-a75aa31b:active{background:#e2e8f0;transform:scale(.94)}.bpm-stepper .step-val.data-v-a75aa31b{min-width:84rpx;height:52rpx;line-height:52rpx;text-align:center;font-size:30rpx;font-weight:800;color:#0f172a;font-variant-numeric:tabular-nums;background:#fef3c7;border-radius:10rpx}.meter-tabs.data-v-a75aa31b{display:flex;gap:8rpx}.meter-tabs .meter-tab.data-v-a75aa31b{height:52rpx;line-height:52rpx;padding:0 18rpx;font-size:22rpx;color:#475569;background:#f1f5f9;border-radius:10rpx;font-weight:600;letter-spacing:1rpx}.meter-tabs .meter-tab.active.data-v-a75aa31b{background:#f59e0b;color:#fff;box-shadow:0 4rpx 10rpx rgba(245,158,11,.3)}.meter-tabs .meter-tab.data-v-a75aa31b:active{transform:scale(.94)}.bottom-row.data-v-a75aa31b{width:100%;margin-top:6rpx;display:flex;align-items:center;justify-content:space-between;padding:0 6rpx}.bottom-link.data-v-a75aa31b{font-size:22rpx;color:#94a3b8;letter-spacing:.5rpx;padding:8rpx 4rpx}.bottom-link.data-v-a75aa31b:active{color:#475569}.bottom-hint.data-v-a75aa31b{font-size:22rpx;color:#94a3b8;letter-spacing:.5rpx}
|
||||
File diff suppressed because one or more lines are too long
+1
-1079
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user