Update metronome.vue
This commit is contained in:
@@ -10,6 +10,26 @@
|
|||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- ===== 音色选择 ===== -->
|
||||||
|
<view class="sound-selector">
|
||||||
|
<view
|
||||||
|
class="sound-option"
|
||||||
|
:class="{ active: currentSound === 'crisp' }"
|
||||||
|
@click="onSoundSelect('crisp')"
|
||||||
|
>
|
||||||
|
<text class="sound-icon">✨</text>
|
||||||
|
<text class="sound-label">清脆</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="sound-option"
|
||||||
|
:class="{ active: currentSound === 'wood' }"
|
||||||
|
@click="onSoundSelect('wood')"
|
||||||
|
>
|
||||||
|
<text class="sound-icon">🪵</text>
|
||||||
|
<text class="sound-label">木鱼</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- ===== 推荐档位(3 选 1) ===== -->
|
<!-- ===== 推荐档位(3 选 1) ===== -->
|
||||||
<view class="presets">
|
<view class="presets">
|
||||||
<view
|
<view
|
||||||
@@ -79,25 +99,34 @@ interface LoopPreset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LOOP_PRESETS: readonly LoopPreset[] = [
|
const LOOP_PRESETS: readonly LoopPreset[] = [
|
||||||
{ id: 'slow', bpm: 80, label: '慢走', desc: '热身 · 恢复' },
|
{ id: 'slow', bpm: 110, label: '慢走', desc: '热身 · 恢复' },
|
||||||
{ id: 'normal', bpm: 110, label: '健走', desc: '日常 · 通勤' },
|
{ id: 'normal', bpm: 130, label: '健走', desc: '日常 · 通勤' },
|
||||||
{ id: 'brisk', bpm: 130, label: '快走', desc: '提速 · 燃脂' },
|
{ id: 'brisk', bpm: 150, label: '快走', desc: '提速 · 燃脂' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const customExpanded = ref<boolean>(false)
|
const customExpanded = ref<boolean>(false)
|
||||||
const currentLoop = ref<LoopId | null>('normal')
|
const currentLoop = ref<LoopId | null>('normal')
|
||||||
|
const currentSound = ref<'crisp' | 'wood'>('crisp')
|
||||||
const walkerCanvasRef = ref<any>(null)
|
const walkerCanvasRef = ref<any>(null)
|
||||||
|
|
||||||
/* click-wood.mp3 (CDN), 木鱼质感的敲击音 */
|
/* 节拍器音效配置 */
|
||||||
const CLICK_WOOD_URL =
|
const SOUND_PRESETS = {
|
||||||
'https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/file/20260526/202605261051282a0a94508.mp3'
|
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 = useMetronome({
|
const fg = useMetronome({
|
||||||
initialBpm: 110,
|
initialBpm: 130,
|
||||||
accentEvery: 2,
|
accentEvery: 2,
|
||||||
clickSrc: CLICK_WOOD_URL,
|
clickSrc: SOUND_PRESETS.crisp.normal,
|
||||||
accentSrc: CLICK_WOOD_URL,
|
accentSrc: SOUND_PRESETS.crisp.accent,
|
||||||
poolSize: 4,
|
poolSize: 4,
|
||||||
// 【完美音画同步核心】
|
// 【完美音画同步核心】
|
||||||
onBeat: (index, isAccent) => {
|
onBeat: (index, isAccent) => {
|
||||||
@@ -163,6 +192,12 @@ const onAccentSelect = (n: number) => {
|
|||||||
fg.setAccentEvery(n)
|
fg.setAccentEvery(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onSoundSelect = (sound: 'crisp' | 'wood') => {
|
||||||
|
currentSound.value = sound
|
||||||
|
const preset = SOUND_PRESETS[sound]
|
||||||
|
fg.updateAudioSrc(preset.normal, preset.accent)
|
||||||
|
}
|
||||||
|
|
||||||
const rootStyle = computed(() => ({
|
const rootStyle = computed(() => ({
|
||||||
'--beat-duration': `${intervalMs.value}ms`,
|
'--beat-duration': `${intervalMs.value}ms`,
|
||||||
}))
|
}))
|
||||||
@@ -232,6 +267,57 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 音色选择器
|
||||||
|
* ============================================================ */
|
||||||
|
.sound-selector {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
gap: 14rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-option {
|
||||||
|
flex: 1;
|
||||||
|
background: $card-bg;
|
||||||
|
border: 2rpx solid $card-border;
|
||||||
|
border-radius: $radius-card;
|
||||||
|
padding: 20rpx 16rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10rpx;
|
||||||
|
box-shadow: $shadow-sm;
|
||||||
|
transition: all 0.18s;
|
||||||
|
|
||||||
|
.sound-icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sound-label {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-2;
|
||||||
|
letter-spacing: 1rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.97);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
|
||||||
|
border-color: $brand;
|
||||||
|
box-shadow:
|
||||||
|
0 8rpx 20rpx rgba(16, 185, 129, 0.18),
|
||||||
|
inset 0 0 0 2rpx rgba(16, 185, 129, 0.4);
|
||||||
|
|
||||||
|
.sound-label {
|
||||||
|
color: $brand-deep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* 三档位推荐
|
* 三档位推荐
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|||||||
Reference in New Issue
Block a user