feat(metronome): walker 卡片改左右布局 + 圆形蒙版小人

老布局问题:视频被困在小白卡里、灰白渐变底跟暗卡片不搭、
左右大量空白、右上角小窗按钮没办法靠属性彻底隐藏。

新布局:
- 圆形蒙版裁切 video,灰白底自动收成柔和光晕,
  原生右上角小窗/投屏按钮被切到圆外,看不见
- video 加 transform:scale(1.08) 让小人居中且裁掉边缘灰边
- 圆外加随节拍脉动的彩色 glow,颜色跟 badge 联动
  (健走绿/快走黄/慢跑橙/快跑红)
- 视频右侧填充节奏数据三行:
  · 每分钟 X 步
  · 每步耗时 X ms
  · 动画速度 X ×
  把空白填上有意义的信息层级

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-25 17:02:47 +08:00
co-authored by Cursor
parent fbf3d47b2a
commit 6be5b4c45d
2 changed files with 171 additions and 31 deletions
+155 -15
View File
@@ -28,7 +28,9 @@
</view>
<view class="walker" :class="{ playing: isPlaying }">
<view class="runner-card">
<view class="runner-orb">
<view class="orb-glow" :class="badgeClass" />
<view class="orb-clip">
<video
id="runner-video"
class="runner-video"
@@ -42,12 +44,38 @@
:show-center-play-btn="false"
:enable-progress-gesture="false"
:show-mute-btn="false"
object-fit="contain"
:picture-in-picture-mode="[]"
object-fit="cover"
@error="onVideoError"
@loadedmetadata="onVideoLoaded"
/>
</view>
</view>
<view class="walker-stats">
<view class="stat-row">
<text class="stat-label">每分钟</text>
<view class="stat-value">
<text class="stat-num">{{ bpm }}</text>
<text class="stat-unit"></text>
</view>
</view>
<view class="stat-row">
<text class="stat-label">每步耗时</text>
<view class="stat-value">
<text class="stat-num">{{ intervalMs }}</text>
<text class="stat-unit">ms</text>
</view>
</view>
<view class="stat-row">
<text class="stat-label">动画速度</text>
<view class="stat-value">
<text class="stat-num">{{ playbackRate.toFixed(2) }}</text>
<text class="stat-unit">×</text>
</view>
</view>
</view>
</view>
</view>
<!-- ===== 控制卡片:BPM + 拍号 ===== -->
@@ -481,32 +509,144 @@ $radius-btn: 20rpx;
width: 100%;
height: 240rpx;
display: flex;
justify-content: center;
align-items: flex-end;
align-items: center;
gap: 28rpx;
padding: 0 8rpx;
}
/* ============================================================
* 跑步小人视频(白卡嵌入)
* 跑步小人 圆形蒙版(裁掉灰白底 + 右上角原生按钮)
* ============================================================ */
.runner-card {
position: absolute;
bottom: 18rpx;
left: 50%;
transform: translateX(-50%);
.runner-orb {
position: relative;
width: 220rpx;
height: 220rpx;
background: #fff;
border-radius: 32rpx;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
/* 外圈彩色光晕,跟 badge 颜色联动 */
.orb-glow {
position: absolute;
inset: -10rpx;
border-radius: 50%;
background: radial-gradient(
circle at 50% 50%,
rgba(34, 197, 94, 0.32) 0%,
rgba(34, 197, 94, 0.12) 55%,
transparent 75%
);
filter: blur(8rpx);
transition: background 0.3s;
&.badge-mid {
background: radial-gradient(
circle at 50% 50%,
rgba(251, 191, 36, 0.32) 0%,
rgba(251, 191, 36, 0.12) 55%,
transparent 75%
);
}
&.badge-strong {
background: radial-gradient(
circle at 50% 50%,
rgba(249, 115, 22, 0.34) 0%,
rgba(249, 115, 22, 0.14) 55%,
transparent 75%
);
}
&.badge-hot {
background: radial-gradient(
circle at 50% 50%,
rgba(239, 68, 68, 0.36) 0%,
rgba(239, 68, 68, 0.14) 55%,
transparent 75%
);
}
}
.walker.playing .orb-glow {
animation: orb-pulse var(--beat-duration, 750ms) ease-in-out infinite;
}
@keyframes orb-pulse {
0%,
100% {
transform: scale(1);
opacity: 0.85;
}
50% {
transform: scale(1.08);
opacity: 1;
}
}
/* 圆形裁切容器 */
.orb-clip {
position: relative;
width: 220rpx;
height: 220rpx;
border-radius: 50%;
overflow: hidden;
background: linear-gradient(160deg, #f5f7fa 0%, #c9d1dc 100%);
box-shadow:
0 12rpx 32rpx rgba(0, 0, 0, 0.25),
inset 0 0 0 1rpx rgba(255, 255, 255, 0.3);
inset 0 0 0 2rpx rgba(255, 255, 255, 0.5),
inset 0 -20rpx 36rpx rgba(0, 0, 0, 0.08);
}
.runner-video {
width: 100%;
height: 100%;
background: #fff;
/* video 比 orb 略大,object-fit:cover 配合定位让小人居中且裁掉灰边 */
transform: scale(1.08);
background: transparent;
}
/* ============================================================
* 走路节奏数据(右侧)
* ============================================================ */
.walker-stats {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
gap: 14rpx;
padding: 8rpx 0;
}
.stat-row {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12rpx;
}
.stat-label {
font-size: 24rpx;
color: $text-3;
letter-spacing: 1rpx;
}
.stat-value {
display: flex;
align-items: baseline;
gap: 4rpx;
}
.stat-num {
font-size: 40rpx;
font-weight: 700;
color: $text-1;
line-height: 1;
font-variant-numeric: tabular-nums;
letter-spacing: -0.5rpx;
}
.stat-unit {
font-size: 22rpx;
color: $text-3;
font-weight: 500;
}
/* ============================================================
Binary file not shown.