feat(metronome): walker 区接入卡通跑步小人,跟 BPM 同步律动
- 替换原来两只脚印为一张跑步小人 PNG(static/training/runner.png)
- 静态 PNG + CSS 律动:
· runner-bob keyframes: 1 拍内做"下蹲→起跳→反向倾斜→落地"
四个阶段,模拟跑步身体起伏
· 上下抖动 ±14rpx, 左右轻摆 ±1.5°,scale Y 微妙压缩
· drop-shadow 让人物有立体感
- 阴影同步:runner-shadow-pulse 跟律动反向变化
(跳起时阴影变小变淡 0.2,落地时变大变浓 0.5)
- 动画 duration 绑定 --step-duration = 60000/bpm
· BPM 60 → 1000ms/次,慢悠悠
· BPM 120 → 500ms/次,正常跑
· BPM 180 → 333ms/次,紧张快跑
注意: 用户提供的文件实际是静态 PNG 不是 SVG 动图,
所以是"画面整体律动"而非真正的 walk cycle 关节运动。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -28,21 +28,13 @@
|
||||
</view>
|
||||
|
||||
<view class="walker" :class="{ playing: isPlaying }">
|
||||
<view class="foot foot-left">
|
||||
<view class="runner-wrap">
|
||||
<image
|
||||
class="footprint"
|
||||
src="/static/training/footprint.svg"
|
||||
class="runner"
|
||||
src="/static/training/runner.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view class="shadow" />
|
||||
</view>
|
||||
<view class="foot foot-right">
|
||||
<image
|
||||
class="footprint"
|
||||
src="/static/training/footprint.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view class="shadow" />
|
||||
<view class="runner-shadow" />
|
||||
</view>
|
||||
<view class="ground" />
|
||||
</view>
|
||||
@@ -414,7 +406,7 @@ $radius-btn: 20rpx;
|
||||
.walker {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 220rpx;
|
||||
height: 240rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
@@ -436,108 +428,86 @@ $radius-btn: 20rpx;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 拟真脚印(SVG 矢量,单笔成型)
|
||||
* 跑步小人(静态 PNG + CSS 律动)
|
||||
* ============================================================ */
|
||||
.foot {
|
||||
.runner-wrap {
|
||||
position: absolute;
|
||||
bottom: 22rpx;
|
||||
width: 96rpx;
|
||||
height: 144rpx;
|
||||
|
||||
.footprint {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform-origin: bottom center;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
position: absolute;
|
||||
bottom: -6rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 70rpx;
|
||||
height: 8rpx;
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
rgba(0, 0, 0, 0.45),
|
||||
transparent 70%
|
||||
);
|
||||
border-radius: 50%;
|
||||
}
|
||||
bottom: 18rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.foot-left {
|
||||
left: calc(50% - 124rpx);
|
||||
}
|
||||
.foot-left .footprint {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
.foot-right {
|
||||
left: calc(50% + 24rpx);
|
||||
.runner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform-origin: bottom center;
|
||||
/* 默认状态保持微妙的呼吸感,不至于完全静止 */
|
||||
transition: filter 0.3s;
|
||||
}
|
||||
|
||||
/* 跟节拍同步:1 拍 1 步,左右各占半周期 */
|
||||
.walker.playing {
|
||||
.foot-left .footprint {
|
||||
animation: foot-step-left var(--step-duration, 750ms) ease-in-out infinite;
|
||||
}
|
||||
.foot-right .footprint {
|
||||
animation: foot-step-right var(--step-duration, 750ms) ease-in-out infinite;
|
||||
animation-delay: calc(var(--step-duration, 750ms) * -0.5);
|
||||
}
|
||||
.foot-left .shadow {
|
||||
animation: shadow-pulse var(--step-duration, 750ms) ease-in-out infinite;
|
||||
}
|
||||
.foot-right .shadow {
|
||||
animation: shadow-pulse var(--step-duration, 750ms) ease-in-out infinite;
|
||||
animation-delay: calc(var(--step-duration, 750ms) * -0.5);
|
||||
}
|
||||
.runner-shadow {
|
||||
position: absolute;
|
||||
bottom: -8rpx;
|
||||
width: 120rpx;
|
||||
height: 12rpx;
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
rgba(0, 0, 0, 0.45),
|
||||
transparent 70%
|
||||
);
|
||||
border-radius: 50%;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
@keyframes foot-step-right {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0) rotate(0deg);
|
||||
/* 跟节拍同步:1 拍 1 次律动 */
|
||||
.walker.playing .runner {
|
||||
animation: runner-bob var(--step-duration, 750ms) ease-in-out infinite;
|
||||
/* 高 BPM 时加速度模糊,让"跑得很快"有视觉表达 */
|
||||
filter: drop-shadow(0 4rpx 12rpx rgba(0, 0, 0, 0.25));
|
||||
}
|
||||
|
||||
.walker.playing .runner-shadow {
|
||||
animation: runner-shadow-pulse var(--step-duration, 750ms) ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 跑步律动:身体上下起伏 + 微妙左右摆动 */
|
||||
@keyframes runner-bob {
|
||||
0% {
|
||||
transform: translateY(0) rotate(0deg) scale(1);
|
||||
}
|
||||
25% {
|
||||
transform: translateY(-40rpx) rotate(8deg);
|
||||
/* 第一拍着地半程,身体最低 */
|
||||
transform: translateY(2rpx) rotate(-1.5deg) scale(1, 0.99);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-2rpx) rotate(0deg);
|
||||
/* 离地最高点,身体抬起 */
|
||||
transform: translateY(-14rpx) rotate(0deg) scale(1, 1.01);
|
||||
}
|
||||
75% {
|
||||
transform: translateY(0) rotate(0deg);
|
||||
/* 另一脚着地半程,反向倾斜 */
|
||||
transform: translateY(2rpx) rotate(1.5deg) scale(1, 0.99);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0) rotate(0deg) scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes foot-step-left {
|
||||
|
||||
@keyframes runner-shadow-pulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scaleX(-1) translateY(0) rotate(0deg);
|
||||
}
|
||||
25% {
|
||||
transform: scaleX(-1) translateY(-40rpx) rotate(8deg);
|
||||
opacity: 0.5;
|
||||
transform: scaleX(1);
|
||||
}
|
||||
50% {
|
||||
transform: scaleX(-1) translateY(-2rpx) rotate(0deg);
|
||||
}
|
||||
75% {
|
||||
transform: scaleX(-1) translateY(0) rotate(0deg);
|
||||
}
|
||||
}
|
||||
@keyframes shadow-pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.55;
|
||||
transform: translateX(-50%) scaleX(1);
|
||||
}
|
||||
25% {
|
||||
opacity: 0.18;
|
||||
transform: translateX(-50%) scaleX(0.5);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
transform: translateX(-50%) scaleX(1.08);
|
||||
/* 跳起时阴影变小变淡 */
|
||||
opacity: 0.2;
|
||||
transform: scaleX(0.7);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Reference in New Issue
Block a user