feat(metronome): 浅色清爽改版 + 三档位 + 阳光节拍音

整页配色从暗色系换成浅色清爽:
- 背景 #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 <cursoragent@cursor.com>
This commit is contained in:
2026-05-25 17:14:45 +08:00
co-authored by Cursor
parent 6be5b4c45d
commit 27c2ada4ec
3 changed files with 286 additions and 434 deletions
+286 -434
View File
@@ -17,99 +17,65 @@
</view>
</view>
<!-- ===== 配速 + 走路动画卡片 ===== -->
<view class="walker-card">
<view class="walker-header">
<view class="badge" :class="badgeClass">
<view class="badge-dot" />
<text>{{ walkerTip }}</text>
</view>
<text class="status">{{ statusHint }}</text>
</view>
<view class="walker" :class="{ playing: isPlaying }">
<view class="runner-orb">
<view class="orb-glow" :class="badgeClass" />
<view class="orb-clip">
<video
id="runner-video"
class="runner-video"
:src="RUNNER_VIDEO_URL"
:muted="true"
:loop="true"
:autoplay="true"
:show-controls="false"
:show-fullscreen-btn="false"
:show-play-btn="false"
:show-center-play-btn="false"
:enable-progress-gesture="false"
:show-mute-btn="false"
: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 class="walker">
<view class="walker-clip">
<video
id="runner-video"
class="runner-video"
:src="RUNNER_VIDEO_URL"
:muted="true"
:loop="true"
:autoplay="true"
:show-controls="false"
:show-fullscreen-btn="false"
:show-play-btn="false"
:show-center-play-btn="false"
:enable-progress-gesture="false"
:show-mute-btn="false"
:picture-in-picture-mode="[]"
object-fit="cover"
@error="onVideoError"
@loadedmetadata="onVideoLoaded"
/>
</view>
</view>
<!-- ===== 控制卡片:BPM + 拍号 ===== -->
<view class="control-card">
<view class="bpm-row">
<view class="step-btn" @click="adjustBpm(-10)">
<text class="step-num">10</text>
</view>
<view class="step-btn" @click="adjustBpm(-1)">
<text class="step-num">1</text>
</view>
<view class="step-btn" @click="adjustBpm(1)">
<text class="step-num">+1</text>
</view>
<view class="step-btn" @click="adjustBpm(10)">
<text class="step-num">+10</text>
</view>
<!-- ===== 推荐档位(3 1) ===== -->
<view class="presets">
<view
v-for="p in PRESETS"
:key="p.id"
class="preset"
:class="{ active: activePreset === p.id }"
@click="applyPreset(p)"
>
<text class="preset-icon">{{ p.icon }}</text>
<text class="preset-name">{{ p.name }}</text>
<text class="preset-bpm">{{ p.bpm }} BPM</text>
<text class="preset-desc">{{ p.desc }}</text>
</view>
</view>
<view class="divider" />
<view class="meter-row">
<text class="meter-label">每组拍数</text>
<view class="meter-options">
<view
v-for="m in METER_OPTIONS"
:key="m.value"
class="meter-btn"
:class="{ active: accentEvery === m.value }"
@click="onMeterChange(m.value)"
>
<text>{{ m.label }}</text>
</view>
<!-- ===== 右下角微调(不显眼) ===== -->
<view class="quick-adjust">
<view class="qa-group">
<view class="qa-btn" @click.stop="adjustBpm(-1)"></view>
<text class="qa-label">微调</text>
<view class="qa-btn" @click.stop="adjustBpm(1)">+</view>
</view>
<view class="qa-divider" />
<view class="qa-group">
<view
v-for="m in METER_OPTIONS"
:key="m.value"
class="qa-meter"
:class="{ active: accentEvery === m.value }"
@click.stop="onMeterChange(m.value)"
>
{{ m.label }}
</view>
<text class="qa-label"></text>
</view>
</view>
</view>
@@ -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(() => {
<style lang="scss" scoped>
/* ============================================================
* 设计 token
* 设计 token —— 浅色清爽
* ============================================================ */
$bg-grad: linear-gradient(180deg, #0a0f1c 0%, #141a2a 100%);
$card-bg: rgba(255, 255, 255, 0.035);
$card-border: rgba(255, 255, 255, 0.06);
$text-1: #f1f5f9;
$text-2: #cbd5e1;
$text-3: #64748b;
$brand: #22c55e;
$brand-deep: #15803d;
$bg-grad: linear-gradient(180deg, #f8fafc 0%, #eef2f7 100%);
$card-bg: #ffffff;
$card-border: rgba(15, 23, 42, 0.06);
$text-1: #0f172a;
$text-2: #475569;
$text-3: #94a3b8;
$brand: #10b981;
$brand-soft: #d1fae5;
$brand-deep: #047857;
$accent: #f59e0b;
$accent-deep: #b45309;
$radius-card: 28rpx;
$radius-btn: 20rpx;
$accent-soft: #fef3c7;
$radius-card: 24rpx;
$radius-btn: 18rpx;
$shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
$shadow-md: 0 12rpx 28rpx rgba(15, 23, 42, 0.08);
/* ============================================================
* 页面容器
* ============================================================ */
.page {
position: relative;
height: 100vh;
box-sizing: border-box;
padding: 30rpx 28rpx 60rpx;
padding: 30rpx 28rpx 80rpx;
background: $bg-grad;
display: flex;
flex-direction: column;
align-items: center;
gap: 24rpx;
gap: 22rpx;
color: $text-1;
overflow: hidden;
}
@@ -309,9 +293,9 @@ $radius-btn: 20rpx;
* ============================================================ */
.stage {
position: relative;
width: 540rpx;
height: 540rpx;
margin-top: 20rpx;
width: 480rpx;
height: 480rpx;
margin-top: 16rpx;
display: flex;
align-items: center;
justify-content: center;
@@ -326,7 +310,7 @@ $radius-btn: 20rpx;
width: 100%;
height: 100%;
border-radius: 50%;
border: 3rpx solid rgba(34, 197, 94, 0.14);
border: 3rpx solid rgba(16, 185, 129, 0.12);
pointer-events: none;
&.playing {
@@ -340,29 +324,29 @@ $radius-btn: 20rpx;
0% {
transform: scale(0.94);
opacity: 0.85;
border-color: rgba(34, 197, 94, 0.45);
border-color: rgba(16, 185, 129, 0.4);
}
100% {
transform: scale(1.18);
opacity: 0;
border-color: rgba(34, 197, 94, 0);
border-color: rgba(16, 185, 129, 0);
}
}
.core {
position: relative;
width: 420rpx;
height: 420rpx;
width: 380rpx;
height: 380rpx;
border-radius: 50%;
background: linear-gradient(140deg, $brand 0%, $brand-deep 100%);
background: linear-gradient(140deg, #34d399 0%, $brand-deep 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow:
inset 0 -16rpx 50rpx rgba(0, 0, 0, 0.22),
inset 0 16rpx 50rpx rgba(255, 255, 255, 0.1),
0 20rpx 50rpx rgba(34, 197, 94, 0.32);
inset 0 -16rpx 50rpx rgba(0, 0, 0, 0.18),
inset 0 16rpx 50rpx rgba(255, 255, 255, 0.18),
0 16rpx 40rpx rgba(16, 185, 129, 0.32);
transition: transform 0.12s, background 0.18s, box-shadow 0.18s;
&.playing {
@@ -370,34 +354,34 @@ $radius-btn: 20rpx;
}
&.playing.accent {
background: linear-gradient(140deg, #fbbf24 0%, $accent-deep 100%);
background: linear-gradient(140deg, #fbbf24 0%, #d97706 100%);
box-shadow:
inset 0 -16rpx 50rpx rgba(0, 0, 0, 0.22),
inset 0 16rpx 50rpx rgba(255, 255, 255, 0.18),
0 20rpx 50rpx rgba(245, 158, 11, 0.4);
inset 0 -16rpx 50rpx rgba(0, 0, 0, 0.18),
inset 0 16rpx 50rpx rgba(255, 255, 255, 0.22),
0 16rpx 40rpx rgba(245, 158, 11, 0.4);
}
.bpm-num {
font-size: 180rpx;
font-size: 168rpx;
font-weight: 800;
color: #fff;
line-height: 1;
letter-spacing: -2rpx;
font-variant-numeric: tabular-nums;
text-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.18);
text-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.16);
}
.bpm-label {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.65);
color: rgba(255, 255, 255, 0.7);
letter-spacing: 6rpx;
margin-top: 10rpx;
font-weight: 600;
}
.ctrl {
margin-top: 26rpx;
height: 76rpx;
margin-top: 22rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
@@ -413,336 +397,204 @@ $radius-btn: 20rpx;
}
}
/* 播放/暂停 icon (CSS 几何) */
/* 播放/暂停 icon */
.icon-play {
width: 0;
height: 0;
border-left: 60rpx solid #fff;
border-top: 38rpx solid transparent;
border-bottom: 38rpx solid transparent;
margin-left: 14rpx;
filter: drop-shadow(0 2rpx 6rpx rgba(0, 0, 0, 0.2));
border-left: 52rpx solid #fff;
border-top: 32rpx solid transparent;
border-bottom: 32rpx solid transparent;
margin-left: 12rpx;
filter: drop-shadow(0 2rpx 6rpx rgba(0, 0, 0, 0.18));
}
.icon-pause {
display: flex;
gap: 18rpx;
height: 70rpx;
gap: 16rpx;
height: 60rpx;
.bar {
width: 18rpx;
width: 16rpx;
height: 100%;
background: #fff;
border-radius: 5rpx;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.2);
border-radius: 4rpx;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.18);
}
}
/* ============================================================
* 走路动画卡片
* 跑步小人(无边框,跟白底融合)
* ============================================================ */
.walker-card {
width: 100%;
background: $card-bg;
border: 1rpx solid $card-border;
border-radius: $radius-card;
padding: 24rpx 28rpx 32rpx;
backdrop-filter: blur(20rpx);
}
.walker-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12rpx;
}
.badge {
display: flex;
align-items: center;
gap: 10rpx;
padding: 8rpx 20rpx;
background: rgba(34, 197, 94, 0.12);
border: 1rpx solid rgba(34, 197, 94, 0.3);
border-radius: 999rpx;
font-size: 24rpx;
color: $brand;
font-weight: 600;
.badge-dot {
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: currentColor;
box-shadow: 0 0 8rpx currentColor;
}
&.badge-mild {
background: rgba(34, 197, 94, 0.12);
border-color: rgba(34, 197, 94, 0.3);
color: #4ade80;
}
&.badge-mid {
background: rgba(251, 191, 36, 0.12);
border-color: rgba(251, 191, 36, 0.3);
color: #fbbf24;
}
&.badge-strong {
background: rgba(249, 115, 22, 0.12);
border-color: rgba(249, 115, 22, 0.3);
color: #fb923c;
}
&.badge-hot {
background: rgba(239, 68, 68, 0.14);
border-color: rgba(239, 68, 68, 0.32);
color: #f87171;
}
}
.status {
font-size: 24rpx;
color: $text-3;
letter-spacing: 1rpx;
}
.walker {
position: relative;
width: 100%;
height: 240rpx;
display: flex;
align-items: center;
gap: 28rpx;
padding: 0 8rpx;
}
/* ============================================================
* 跑步小人 圆形蒙版(裁掉灰白底 + 右上角原生按钮)
* ============================================================ */
.runner-orb {
position: relative;
width: 220rpx;
height: 220rpx;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
align-items: 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;
.walker-clip {
/* 圆形蒙版裁掉 video 的灰白边 + 隐藏右上角原生小窗按钮 */
width: 220rpx;
height: 220rpx;
border-radius: 50%;
overflow: hidden;
background: linear-gradient(160deg, #f5f7fa 0%, #c9d1dc 100%);
box-shadow:
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%;
/* video 比 orb 略大,object-fit:cover 配合定位让小人居中且裁掉灰边 */
transform: scale(1.08);
transform: scale(1.1);
background: transparent;
}
/* ============================================================
* 走路节奏数据(右侧)
* 三档位推荐
* ============================================================ */
.walker-stats {
.presets {
width: 100%;
display: flex;
gap: 16rpx;
}
.preset {
flex: 1;
background: $card-bg;
border: 2rpx solid $card-border;
border-radius: $radius-card;
padding: 22rpx 12rpx 20rpx;
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;
}
/* ============================================================
* 控制卡片(BPM + 拍号)
* ============================================================ */
.control-card {
width: 100%;
background: $card-bg;
border: 1rpx solid $card-border;
border-radius: $radius-card;
padding: 28rpx;
backdrop-filter: blur(20rpx);
}
.bpm-row {
display: flex;
gap: 14rpx;
}
.step-btn {
flex: 1;
height: 96rpx;
border-radius: $radius-btn;
background: rgba(255, 255, 255, 0.05);
border: 1rpx solid rgba(255, 255, 255, 0.08);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
&:active {
background: rgba(34, 197, 94, 0.18);
border-color: rgba(34, 197, 94, 0.5);
transform: scale(0.96);
}
.step-num {
font-size: 38rpx;
color: $text-1;
font-weight: 700;
font-variant-numeric: tabular-nums;
letter-spacing: -1rpx;
}
}
.divider {
height: 1rpx;
background: rgba(255, 255, 255, 0.06);
margin: 24rpx 0;
}
.meter-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
}
.meter-label {
font-size: 26rpx;
color: $text-3;
letter-spacing: 1rpx;
}
.meter-options {
display: flex;
gap: 10rpx;
background: rgba(255, 255, 255, 0.04);
padding: 6rpx;
border-radius: 999rpx;
border: 1rpx solid rgba(255, 255, 255, 0.05);
}
.meter-btn {
min-width: 84rpx;
height: 60rpx;
padding: 0 20rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 6rpx;
box-shadow: $shadow-sm;
transition: all 0.18s;
text {
.preset-icon {
font-size: 44rpx;
line-height: 1;
margin-bottom: 6rpx;
}
.preset-name {
font-size: 30rpx;
font-weight: 700;
color: $text-1;
letter-spacing: 2rpx;
}
.preset-bpm {
font-size: 24rpx;
color: $text-2;
font-weight: 600;
font-variant-numeric: tabular-nums;
}
.preset-desc {
font-size: 20rpx;
color: $text-3;
letter-spacing: 1rpx;
margin-top: 2rpx;
}
&:active {
transform: scale(0.97);
}
&.active {
background: $brand;
box-shadow: 0 4rpx 12rpx rgba(34, 197, 94, 0.4);
background: $brand-soft;
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);
text {
color: #fff;
.preset-name {
color: $brand-deep;
}
.preset-bpm {
color: $brand-deep;
}
.preset-desc {
color: $brand;
}
}
}
/* ============================================================
* 右下角微调(不显眼)
* ============================================================ */
.quick-adjust {
position: absolute;
right: 28rpx;
bottom: 22rpx;
display: flex;
align-items: center;
gap: 14rpx;
padding: 10rpx 18rpx;
background: rgba(255, 255, 255, 0.7);
border: 1rpx solid rgba(15, 23, 42, 0.05);
border-radius: 999rpx;
backdrop-filter: blur(10rpx);
opacity: 0.7;
transition: opacity 0.2s;
&:active {
opacity: 1;
}
}
.qa-group {
display: flex;
align-items: center;
gap: 6rpx;
}
.qa-label {
font-size: 20rpx;
color: $text-3;
margin: 0 4rpx;
letter-spacing: 1rpx;
}
.qa-btn {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background: rgba(15, 23, 42, 0.05);
color: $text-2;
font-size: 26rpx;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
&:active {
background: rgba(16, 185, 129, 0.18);
color: $brand-deep;
}
}
.qa-meter {
min-width: 36rpx;
height: 36rpx;
padding: 0 10rpx;
border-radius: 999rpx;
font-size: 22rpx;
color: $text-3;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
transition: all 0.15s;
&.active {
background: $brand;
color: #fff;
box-shadow: 0 2rpx 6rpx rgba(16, 185, 129, 0.4);
}
}
.qa-divider {
width: 1rpx;
height: 22rpx;
background: rgba(15, 23, 42, 0.08);
}
</style>
Binary file not shown.
Binary file not shown.