docs: add VerticalProgressBar component design spec
This commit is contained in:
parent
edadb0e082
commit
8178799de7
@ -0,0 +1,227 @@
|
||||
# VerticalProgressBar 组件设计
|
||||
|
||||
**日期**:2026-06-02
|
||||
**作者**:zerosaturation
|
||||
**状态**:设计中
|
||||
|
||||
## 背景与目标
|
||||
|
||||
`support-activity` 页面(生日应援活动)目前仅在 [ThemeBanner.vue](../../../frontend/pages/support-activity/components/ThemeBanner.vue) 右下角以纯文字形式展示进度:
|
||||
|
||||
```
|
||||
当前进度 19,901,123 / 19,911,005
|
||||
```
|
||||
|
||||
缺少一个**直观的、可视化**的进度呈现。本次新增一个独立的竖向进度条组件 `VerticalProgressBar`,承担"展示当前活动进度"这一职责,并实时反映后端轮询数据。
|
||||
|
||||
## 范围
|
||||
|
||||
- **In**:
|
||||
- 新增可复用组件 `VerticalProgressBar.vue`
|
||||
- 包含竖向进度条 + 当前/目标数字
|
||||
- 当前进度圆随实时数据沿轨道自下而上移动
|
||||
- **Out**:
|
||||
- 不修改 `ThemeBanner.vue`
|
||||
- 不在 `ThemeBanner.vue` 中嵌入此组件
|
||||
- 视觉细节(颜色、圆角、阴影等)不调优,由用户后续自行调整
|
||||
|
||||
## 组件设计
|
||||
|
||||
### 文件位置
|
||||
|
||||
`frontend/pages/support-activity/components/VerticalProgressBar.vue`
|
||||
|
||||
### Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `current` | Number | 0 | 当前进度值 |
|
||||
| `target` | Number | 100 | 目标进度值 |
|
||||
| `barHeight` | String | `'200rpx'` | 轨道总高度 |
|
||||
| `barWidth` | String | `'16rpx'` | 轨道宽度 |
|
||||
| `circleSize` | String | `'60rpx'` | 进度圆直径 |
|
||||
| `fillColor` | String | `'linear-gradient(180deg, #FFD700, #FFA500)'` | 填充渐变 |
|
||||
| `trackColor` | String | `'rgba(255,255,255,0.2)'` | 轨道底色 |
|
||||
| `textColor` | String | `'#FFD700'` | 当前数字颜色 |
|
||||
| `targetColor` | String | `'rgba(255,255,255,0.8)'` | 目标数字颜色 |
|
||||
| `showText` | Boolean | `true` | 是否显示数字(方便单测与单用) |
|
||||
|
||||
### 视觉结构
|
||||
|
||||
```
|
||||
19,911,005 ← 目标数字(位于目标圆上方)
|
||||
┌─┐
|
||||
│ │ ← 目标圆(固定在 bar 顶部)
|
||||
├─┤
|
||||
│ │ ← 未填充轨道(trackColor)
|
||||
│ │
|
||||
│ │
|
||||
┌─┴─┐
|
||||
│ │
|
||||
│19,│ ← 当前圆(带数字在内部,随 progress 上移)
|
||||
│901│
|
||||
│ │
|
||||
├─==┤ ← 已填充(fillColor,从底部至当前圆底部)
|
||||
│ │
|
||||
│ │
|
||||
└───┘
|
||||
```
|
||||
|
||||
### 模板
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view class="v-progress">
|
||||
<text v-if="showText" class="v-target-text">{{ formattedTarget }}</text>
|
||||
<view
|
||||
class="v-track"
|
||||
:style="{ width: barWidth, height: barHeight }"
|
||||
>
|
||||
<view class="v-fill" :style="{ height: fillHeight }" />
|
||||
<view class="v-circle v-target-circle" :style="{ width: circleSize, height: circleSize }" />
|
||||
<view
|
||||
class="v-circle v-current-circle"
|
||||
:style="{ top: circleTop, width: circleSize, height: circleSize }"
|
||||
>
|
||||
<text v-if="showText" class="v-current-text">{{ formattedCurrent }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 计算公式
|
||||
|
||||
```js
|
||||
// 进度比例(0~1)
|
||||
const ratio = computed(() => {
|
||||
if (props.target === 0) return 0
|
||||
return Math.min(props.current / props.target, 1)
|
||||
})
|
||||
|
||||
// 填充高度:从底部起,占 bar 的 ratio × 100%
|
||||
const fillHeight = computed(() => ratio.value * 100 + '%')
|
||||
|
||||
// 当前圆位置:从顶部起,(1 - ratio) × 100%
|
||||
const circleTop = computed(() => (1 - ratio.value) * 100 + '%')
|
||||
|
||||
// 数字本地化(避免每帧重建)
|
||||
const formattedCurrent = ref(props.current.toLocaleString())
|
||||
const formattedTarget = ref(props.target.toLocaleString())
|
||||
|
||||
watch(() => props.current, (v) => { formattedCurrent.value = v.toLocaleString() })
|
||||
watch(() => props.target, (v) => { formattedTarget.value = v.toLocaleString() })
|
||||
```
|
||||
|
||||
> **公式可替换点**:未来若要换成非线性/缓动/阈值公式,仅需替换 `ratio` 这一处计算。
|
||||
|
||||
### CSS 关键点
|
||||
|
||||
```css
|
||||
.v-progress {
|
||||
position: fixed;
|
||||
left: 24rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
/* 其它样式留空,方便用户自行调 */
|
||||
}
|
||||
|
||||
.v-track {
|
||||
position: relative;
|
||||
background: v-bind(trackColor);
|
||||
border-radius: 999rpx;
|
||||
overflow: visible; /* 关键:让圆能露出轨道边界 */
|
||||
}
|
||||
|
||||
.v-fill {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: v-bind(fillColor);
|
||||
border-radius: 999rpx;
|
||||
transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.v-circle {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.v-target-circle {
|
||||
top: 0;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
/* 不放数字 */
|
||||
}
|
||||
|
||||
.v-current-circle {
|
||||
background: v-bind(fillColor);
|
||||
color: v-bind(textColor);
|
||||
transition: top 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.v-current-text,
|
||||
.v-target-text {
|
||||
font-size: 20rpx;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.v-target-text {
|
||||
color: v-bind(targetColor);
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
```
|
||||
|
||||
### 行为
|
||||
|
||||
1. 组件挂载后,初始 `ratio` 决定当前圆位置和填充高度
|
||||
2. 父组件更新 `current` 时,`formattedCurrent` 通过 watch 重算(避免每帧 `toLocaleString`)
|
||||
3. 当前圆 `top` 与 `.v-fill` 高度过渡时长 0.4s,使用 ease 曲线
|
||||
4. `target = 0` 时 `ratio = 0`,当前圆停在底部
|
||||
|
||||
## 边界与错误处理
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| `target = 0` | `ratio = 0`,圆停在底部,无报错 |
|
||||
| `current > target` | `ratio` 被 `Math.min` 截断为 1,圆停在顶部 |
|
||||
| `current < 0` | 透传,UI 表现圆停在 `ratio < 0` 的位置(视觉上圆略微被遮挡) |
|
||||
| 父组件不传任何 prop | 使用全部默认值(current=0, target=100)|
|
||||
|
||||
## 使用方式
|
||||
|
||||
**不修改 `ThemeBanner.vue`**,在 `support-activity/index.vue` 中以兄弟节点方式使用:
|
||||
|
||||
```vue
|
||||
<VerticalProgressBar
|
||||
:current="progressData.current"
|
||||
:target="progressData.target"
|
||||
/>
|
||||
```
|
||||
|
||||
页面已通过 `progressManager` 实时更新 `progressData.current`,因此组件会自动响应。
|
||||
|
||||
## 测试
|
||||
|
||||
> 单元测试不在本次范围。后续可补:
|
||||
>
|
||||
> - `ratio` 计算(target=0、current>target、current=负数等)
|
||||
> - watch 触发 `toLocaleString` 次数
|
||||
> - 视觉回归(手动)
|
||||
|
||||
## 待办与未来扩展
|
||||
|
||||
- [ ] 替换为非线性/缓动公式(按用户后续设计)
|
||||
- [ ] 加入 stage 段位刻度(沿 bar 显示 N 段)
|
||||
- [ ] 完成后切换为"庆祝态"动画
|
||||
- [ ] 提取公共 composable(`useRatio`)便于其他页面复用
|
||||
Loading…
Reference in New Issue
Block a user