This commit is contained in:
Your Name
2026-08-17 13:59:12 +08:00
parent 79601ea176
commit 49309b7c57
62 changed files with 82 additions and 47023 deletions
@@ -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
File diff suppressed because one or more lines are too long
@@ -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}
File diff suppressed because one or more lines are too long
@@ -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
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
File diff suppressed because one or more lines are too long