From 27c2ada4ec8e9c5698991f58a2a99606d7b3c51e Mon Sep 17 00:00:00 2001 From: long <452591453@qq.com> Date: Mon, 25 May 2026 17:14:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(metronome):=20=E6=B5=85=E8=89=B2=E6=B8=85?= =?UTF-8?q?=E7=88=BD=E6=94=B9=E7=89=88=20+=20=E4=B8=89=E6=A1=A3=E4=BD=8D?= =?UTF-8?q?=20+=20=E9=98=B3=E5=85=89=E8=8A=82=E6=8B=8D=E9=9F=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 整页配色从暗色系换成浅色清爽: - 背景 #f8fafc → #eef2f7 渐变 - 卡片纯白 + 柔和阴影 - 主圆 emerald 翡翠绿,重音切琥珀色 视频区域: - 移除卡片外框/光晕/阴影,保留圆形蒙版 (圆形蒙版仍负责裁掉 video 灰白边 + 隐藏微信原生右上角小窗按钮) - video 透明背景,跟白底融合 新增 3 档位推荐(主操作): - 慢走 80 BPM · 健走 110 BPM · 快走 130 BPM - 横排白卡,选中变浅绿 + emerald 描边 - 一键同步 BPM 和拍号,免去手动调 右下角微调(次要操作): - absolute 定位胶囊,7 成透明 - ± BPM 微调 + 2/3/4 拍号 - 不抢主视觉,需要精确时才点 替换节拍音色(ffmpeg 合成): - 普通拍 C6 1046Hz + C7 泛音,清脆"叮" - 重音 G6 1568Hz + D7 + G7,更明亮"叮咚" - 加 5ms attack + exp decay + 轻微回声,模拟钢片琴 - 旧的阴沉鼓点声备份在 audio/_backup/ Co-authored-by: Cursor --- TUICallKit-Vue3/pages/training/metronome.vue | 720 +++++++----------- .../static/training/audio/click-wood.mp3 | Bin 4697 -> 3179 bytes .../static/training/audio/click.mp3 | Bin 3444 -> 2552 bytes 3 files changed, 286 insertions(+), 434 deletions(-) diff --git a/TUICallKit-Vue3/pages/training/metronome.vue b/TUICallKit-Vue3/pages/training/metronome.vue index 8ab0863b..7a2f443a 100644 --- a/TUICallKit-Vue3/pages/training/metronome.vue +++ b/TUICallKit-Vue3/pages/training/metronome.vue @@ -17,99 +17,65 @@ - - - - - - {{ walkerTip }} - - {{ statusHint }} - - - - - - - - - - - - 每分钟 - - {{ bpm }} - - - - - 每步耗时 - - {{ intervalMs }} - ms - - - - 动画速度 - - {{ playbackRate.toFixed(2) }} - × - - - + + + + - - - - - −10 - - - −1 - - - +1 - - - +10 - + + + + {{ p.icon }} + {{ p.name }} + {{ p.bpm }} BPM + {{ p.desc }} + - - - - 每组拍数 - - - {{ m.label }} - + + + + + 微调 + + + + + + + {{ m.label }} + @@ -129,13 +95,42 @@ const RUNNER_VIDEO_URL = 'https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/video/20260525/20260525164014ef89f8862.mp4' /** - * 视频原速对应的 BPM 基准 - * runner.mp4 时长 0.97s, 一个 walk cycle (左右脚各一次) ≈ 124 BPM 步频 - * 用户调速时 playbackRate = bpm / VIDEO_BASE_BPM - * 微信小程序限制 playback-rate 范围 [0.5, 2.0] + * 视频原速对应的 BPM 基准(参见 hooks 实现注释) + * playback-rate 范围 [0.5, 2.0] */ const VIDEO_BASE_BPM = 124 +/** + * 推荐档位:覆盖大多数老人健走场景,一键切换 BPM + 拍号 + * 不在档位的中间数值用右下角微调即可 + */ +const PRESETS = [ + { + id: 'slow', + icon: '🚶', + name: '慢走', + bpm: 80, + accent: 4, + desc: '热身 · 恢复', + }, + { + id: 'normal', + icon: '🏃‍♀️', + name: '健走', + bpm: 110, + accent: 4, + desc: '日常 · 通勤', + }, + { + id: 'brisk', + icon: '🏃', + name: '快走', + bpm: 130, + accent: 4, + desc: '提速 · 燃脂', + }, +] as const + const METER_OPTIONS = [ { label: '2', value: 2 }, { label: '3', value: 3 }, @@ -151,7 +146,6 @@ const metronome = useMetronome({ const { bpm, isPlaying, - beatIndex, isAccent, accentEvery, intervalMs, @@ -180,6 +174,19 @@ const togglePlay = () => { } } +/* 当前命中的档位(BPM + 拍号都匹配才算选中) */ +const activePreset = computed(() => { + const hit = PRESETS.find( + (p) => p.bpm === bpm.value && p.accent === accentEvery.value, + ) + return hit?.id ?? null +}) + +const applyPreset = (p: (typeof PRESETS)[number]) => { + setBpm(p.bpm) + setAccentEvery(p.accent) +} + /* 视频实例与 BPM 同步 */ let videoCtx: UniApp.VideoContext | null = null @@ -190,7 +197,6 @@ const playbackRate = computed(() => { onMounted(() => { videoCtx = uni.createVideoContext('runner-video') - /* 进页面默认让视频保持暂停,跟节拍器 isPlaying 状态同步 */ setTimeout(() => videoCtx?.pause(), 50) }) @@ -205,13 +211,11 @@ const onVideoError = (e: any) => { const onVideoLoaded = (e: any) => { console.log('[runner-video] metadata loaded:', e?.detail) - /* 元数据加载完后再设 playbackRate,部分基础库初始化时设非 1.0 会卡 */ try { videoCtx?.playbackRate?.(playbackRate.value) } catch (_) {} } -/* 节拍器状态变化 → 同步视频播放/暂停 */ watch(isPlaying, (playing) => { if (playing) { videoCtx?.play() @@ -220,7 +224,6 @@ watch(isPlaying, (playing) => { } }) -/* BPM 变化 → 调整视频播放速度(部分基础库需要手动 API 调用) */ watch(playbackRate, (rate) => { try { videoCtx?.playbackRate?.(rate) @@ -232,29 +235,6 @@ const rootStyle = computed(() => ({ '--step-duration': `${intervalMs.value}ms`, })) -const statusHint = computed(() => { - if (isPlaying.value) { - const cur = ((beatIndex.value - 1) % accentEvery.value) + 1 - return `第 ${cur} 拍 / 共 ${accentEvery.value} 拍` - } - return '轻点中央圆点开始' -}) - -const walkerTip = computed(() => { - if (bpm.value < 70) return '慢走 · 热身' - if (bpm.value < 100) return '健走 · 日常' - if (bpm.value < 130) return '快走 · 提速' - if (bpm.value < 170) return '慢跑 · 有氧' - return '快跑 · 高强' -}) - -const badgeClass = computed(() => { - if (bpm.value < 100) return 'badge-mild' - if (bpm.value < 130) return 'badge-mid' - if (bpm.value < 170) return 'badge-strong' - return 'badge-hot' -}) - onShow(() => { preload() }) @@ -273,33 +253,37 @@ onUnmounted(() => { diff --git a/TUICallKit-Vue3/static/training/audio/click-wood.mp3 b/TUICallKit-Vue3/static/training/audio/click-wood.mp3 index a6127c85b8409bcc86b1111f68ca3893b5f1f689..4f89ad7affb658b05b95830581314a94625b720a 100644 GIT binary patch literal 3179 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 literal 4697 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 diff --git a/TUICallKit-Vue3/static/training/audio/click.mp3 b/TUICallKit-Vue3/static/training/audio/click.mp3 index a25fa2b0fa13017eb572999424b3756d2c663466..d56c0dd0d722b76eee79624ac12081f0a27bb4d4 100644 GIT binary patch literal 2552 zcmc(hiBr?p7RT=|za%8A2?PiNF-nXfApF7-(ZPaj0*%igC;_Vc6pRS6s0D*sNg|8T z6cLmLMTj6GEeaL{5iMa67f?Y_q-FD|Xpz1LxFXB1;W?do{005YoqNu`ckY}s=X~eR z_49JXgL$li0t3A@)&Kxluq{c6ZzcPEh~f@{8*iCyv*6ntMPC0CER# zTR`{%QWl_ENYes_7l18bKC&js2u+d>F7u1OVc~xG;*vXJUYt7g_x3++HHB*fu)#M+ zaQbf)YnyeR-7A`KKRm+8FW9MO(p|KSL9@C~-XhCxgOpjkYMlv&=% z(A!n9G&F&NL|L>wud&R#x4O^CO!=pCUPM}_(1C}l+SZ~%UatS57SD5GMj3Cb^cv!4 z0(H4QR)!rG0|yQq{BHJ@-?p2OmhECUJZwx%NZ|lx1Iz!L&zDV31LT#9ZE_y9<7{rX zT)pkh@m+)G11dXxf^}(dmsk4z&p3DB2!pli06TBwE-{!&9S*t><>hM3P2^9zm2Fs79=xo^=Kk2{D@kM6JMO>=JN<5%lr^EugHzX2 zBOmN0*>+m;6pFFUpLBnraulW>GtD%H4oIhKd$>mlNH5f(=u$9(P48{9fU|pq<9gU# z*i^aYW>qW)QYWBwgb{6dm-v)B@tJTWf@T1_528dElCr{It$vZTg?qks8Np+NXH(;4 zY;zMfouZM??5XD)VrQE~UuUa!bg<~Vs04BbgJQhOWdrV!-b4nk5wcqKQsJ^Y0Is}w zj)&?5QunOqnGqbxl+y&<9w|xZ7?_etUBMBwxzb4>@T_g`a`V1k*{K^Q;CWc6b8PhK zq}#2c17=EJYoSG}dyt=qV?r%b>HUU&e0KHWSVrh9W2|xS{_XqRPCjUR`5vk8TWQ}A z?>f3MZZLVE{BBe$6o>`fR}o8C^rE3XGd{@c2cI+7tV zH*ZHTDhkEytA*mVX_ClP*7-%Maa00{0qTT`@N5E;PK5KI93#X9=c$DRATzqK?b{pt z#m=t98X&Ha}PNv8{jex7u0 z)M%=#82ip`u4;4fjbBguCELx_n`ky?P|_V$jU&7I)g?rUb2O~>cWCF- zghn2b*h(6YKvv;vAkR+!Ug9(|Ep>%?uW!%mv=vz|IE+ElQq?6?;Jv=1o0=6F} zrZcSd&wN#^DFJrK=JiZfWBBY&QGJjeFf41Y zse*}zr4mDYJ2d~qYPpiNWS#)6HW!A-v>h4o8Ihgpe? zF#TB!3!9hR8pyy&Soa{lQ@%CJNY*q(`UsrwGu)`;n}S zYNy8&%#IvgPPc^oq_4HLflIJ^n#UwU_=#8!U@TA#v+VY9a zXvHb+=@-A=*!i!%{Ks#iqmSl{u3J*G#(Q&TuWTvF`=NPY`_tqh;`S5Fk=(LH;@wM# zv6*Zet@Ncb%|lZr%sFbr1uV6SrH7z=UQY-=qwcKu!E#r;7cNUf*H^XQnfsR!r&3^^}OVK zfWDW1;l+bL;i6YQkYSc8W-0kxNTBHK!#u0;Fh{6G7m1M4{7wghWo28h_u6a-#RuW21wyo0@tt zEb`|A_FHGS_6;UA%~h=~p0!LlGmzXgR~-Hhds}*Ga;Rv%%QJpAgLGRmdi4dW7EHCN z`%T=}mN*&s5K6?`pV+mRJME4cq>g~*I-Id1G|WK!;2OBvn@zBS+*ybNT#6s&z@FD0_O^l^8+KQpZ~g8jpd0GL)n3gv$wy&9IAG$vg6{i*f54>aEtVAlX<(EhHsIj4uZ2 ziRM3n1Jv~yKfKgs9D1xAFZ*ME!26kXC68(G6iE&ilN+;38&XXsPpE(oQ^S%v< e)~<9a?LC>0wKO-kC{(a^y|<(0hlTv~5AYXhLn2H7 literal 3444 zcmds)c{J4BAIHBl24jgyVq_|!j5HXGERV8`eF+)HppYbk45FfDEZL?+X)M_)lwD0| zOl5tBY;8=o5RK_cv`95szQ1|?cz*wNe*gUbdS2(;bI-Z&d(Qp5Kj+^2xffi`4g&rM z#FG&xgi~aN4F&+4VgTZAxcobKzXSXo!nItTES#;q1T%ExK03;JQv1$7O7FLst)*mWB6KNmt*Y96B=4a8S{W{FVfUS7dw3h;#u8hkP3o7| z_arR7=u)UuIzPA!g4}Il3&)zkl&PpJmvR1wB_#jQB>H2*t!srW^z~RjPm7{K5*IVt zaHu$xVN9v@&W^kKtMuo@z1XO%oeZDM&Y7x$4ZkWjcHuDYOC{swLf%So!lj}7z2@== zJWXggjc&gP5kGYa0dv9nb1X7Y5CfE6O4{AMc7V|UOqX%x=?hglw4cX!f)wvU;GFYK z@5nOu07;qMsS=6G(HeiDhBIPLW-A0hM>5*K<)kU4YZG^afp$_BsYgKchXO~E7e#q-}lkVm2(iZ zx7VtiKfnItEOEZ&NFW7l!d;(u@U02sm4u0?i(j5(26JO|tSIN4Y9`*8v3LFqIQa15 zR8OEn+~vn_)-0;H_i=$07UJ(J={6kYAVkQU@0^Uib%KhZFmw2 z#dw9p{$=7}(*C~0$Fl(NnPDd45L&Ol*IKQ=x$u@P=KE~sZHJP1 z!Y1;NK4(j~QP z*rhk&HkM|8_T6WX4OA%To|ZY5JpeTlc-Kd3w*4Yj^Zn#<@ZNp(-tKI@{G|XKJ{ZsR z8>)FMrqYRrfAd(>m-fd?7!Tc=!)Dt_yGrWmB^TIvJd+X=Q^#-f9Xz4i`4qvDq6|dq zn~mHm?QSKU?8_}a`zo~g;RAJ}m+@X)rX0&pDGlp$q_Jkl!eizZr3*=6N;09Td2vVuu&p>5x?m+7hbwi%)quleXCTkBegs(u8r@n;NW*#&52 z5&w6pR%L%_!mOILpApc+8c}rAhJl%($4Efq3sLcojY)DC0vZO2QB;eBYtT$zBQBdI| z?$CX5WAD@8XM&4zcp4{>sP|q1fQY+xH%Ih3L&VB0zK1**Fz-S_wq3KHv*Bgt7+HEt zR=cZ7#^`--9miD@SN}-ACq1p9>E0!u&wiHN`KDsf*2J*+x%||m>8_TFzUoV}(^O<( zv%*bR%*1w{86(eDg)ue%gJq^|?8TWJEQmeyPF!S&$dXTgWxFtuJ8?60Rxs7Hsefg9 znkR_+3WChMvlrvfP=5&WUfIpce>X-Kyqm8XzAY?btK38+0%8sT-4GyYgzI?~)9atV z*l|oEj)1*&OfBo-C3y-dRXl~bz-}q@hR8*DFo`uig;I736^;>Jo{Q*sdqPhw;-75yYVKD@I)&4ZNmEKqZ)5^lN2~P zdiYXAchf1DYc@oLP?nt0Vea`>`~5PwC?~U8G0n?|%iL!bJQHTuQ?(C=eoA z(Pz(L+jN3Z8wh#8sF?9chl z$-s$_8iV7BO3Qm7#Kz2-*<=-bVxDhjv3+myJRXLY&)=0)ahQbpdJx%BS)NfQ{U_?I zrJS(VNjT7p)HQYJYgL7zD(yj?w$_T_q4b?6Wp0T0gY3q&iOo#wyp1ItMb#(g#yB%} zn|czp>~_Qj{A^w1Ot9iUIa|Hb?q4Omi3vYA=$zt6nmyGaGf}qIl4;_!Jyw53C+JkX zZzZQ&`yFp|R>L>EGVCz(^*NkEozq*ZCV5fR3#=fJF808Sy^r3M|E+%emBo@P3oSJ2 z^|qYPiQ>nSb^#Bekgb7AABkPY1>=}w_&RP0w`9GQ z^|fRQnZRR3v_*}>L!)HtWba=ozgJ2Hd49RvHgMH4YQ_9qaerY@C@S?cANNeS<%dpq zw)|?NY8jZ`DZHR2$4YSVezcOx5-J0$WDpZqwDxsD+R|GcULLwo6GHB)Lf=FW+tg zER`>l*EyVFz6mKblpG$xH;u@ss;a697;F%bBgoNpcUyUNVajq@T}4Zz*Hmzkd;ztj zI*keM_gRXrMlJB_SY#iTT5wl!yc;g&aFnFF{AV|cwW;xKgTuN$MvH9y?bUY(j?yrg zmc?@ssqL!0IHMf5qp1-}4Fpf_&3Z2a;pm1^q5amAga+_!;R>1OS&%n&gAhURniGzL z>x5GTQqo0+(hYa(2(%6~U-FFR!yE1Oq6akZSx2Iu-}zJFzwg=Ve+A%bWYCQ4H%+&f jV1PfA{IvSi+cMz|2q_mHd=&sNpb5VV{|cl3ZxjCkZ+u!i