# 288度对称圆环进度条实现原理
## 概述
使用 HTML5 Canvas API 实现的对称 288° 圆环进度条,支持颜色渐变和平滑动画。
## 核心技术点
### 1. Canvas 绘制基础
| 技术 | 说明 |
|------|------|
| 绘制方法 | `ctx.arc()` 绘制圆弧 |
| 样式设置 | `strokeStyle`、`lineWidth`、`lineCap` |
| 坐标系统 | 以画布中心为原点,使用极坐标计算 |
### 2. 角度配置
| 参数 | 值 | 说明 |
|------|-----|------|
| 起始角度 | -144° | 7点钟方向(相对于12点) |
| 结束角度 | 144° | 5点钟方向(相对于12点) |
| 总角度 | 288° | 占完整圆的 80% |
| 空白区域 | 72° | 144° 到 216° 之间 |
**角度计算公式**:
```
角度 = -144° + 进度百分比 × 288° / 100
```
### 3. CSS 旋转处理
```css
.progress-wrapper {
transform: rotate(-90deg); /* 向左旋转90度 */
}
.progress-info {
transform: rotate(90deg); /* 文字反向旋转保持正常 */
}
```
**原理**:整个容器旋转 -90°,使 Canvas 的 0° 基准位于 12 点钟方向,文字区域反向旋转保持可读性。
### 4. 颜色渐变实现
#### 颜色节点配置
```javascript
const colorStops = [
{percent: 0, color: '#FFC900'}, // 黄色
{percent: 11, color: '#FFC900'},
{percent: 32, color: '#13A6FF'}, // 蓝色
{percent: 50, color: '#806EFF'}, // 紫色
{percent: 66, color: '#FF8EEC'}, // 粉色
{percent: 89, color: '#FE4F51'}, // 红色
{percent: 100, color: '#FE4F51'}
];
```
#### 颜色插值算法
1. 根据进度百分比找到对应的两个颜色节点
2. 计算节点间的比例 `t = (percent - startPercent) / (endPercent - startPercent)`
3. RGB 线性插值:`R = R1 + (R2 - R1) × t`
### 5. 分段绘制渐变
**核心思路**:将圆弧分成多段,每段使用不同颜色,实现平滑渐变效果。
```javascript
// 计算分段数量(根据进度动态调整)
const segmentCount = Math.max(20, Math.floor(progress * 0.5));
// 分段绘制
for (let i = 0; i < segmentCount; i++) {
const segmentStart = startAngleRad + (angleRange * i / segmentCount);
const segmentEnd = startAngleRad + (angleRange * (i + 1) / segmentCount);
const segmentCenterPercent = (i + 0.5) / segmentCount * progress;
const segmentColor = getColorForPercent(segmentCenterPercent);
ctx.strokeStyle = segmentColor;
ctx.arc(centerX, centerY, radius, segmentStart, segmentEnd);
ctx.stroke();
}
```
### 6. 动画实现
#### 缓动函数
```javascript
const easing = 0.1;
currentProgress += (targetProgress - currentProgress) * easing;
```
#### 动画循环
- 使用 `requestAnimationFrame` 实现平滑动画
- 当差值小于 0.1 时停止动画
- 每帧调用 `drawProgress()` 重绘画布
### 7. 绘制流程
```
drawProgress(progress)
├── clearRect() // 清空画布
├── drawBackgroundRing() // 绘制背景圆环和空白区域指示
├── 计算结束角度
├── 分段绘制进度条(渐变)
├── 绘制起点/终点标记(100%时)
├── drawProgressText() // 绘制中心文字
└── updateUIDisplay() // 更新UI元素
```
## 关键函数说明
| 函数 | 功能 |
|------|------|
| `drawProgress(progress)` | 主绘制函数,绘制完整进度条 |
| `getColorForPercent(percent)` | 根据百分比获取插值颜色 |
| `interpolateColor(c1, c2, t)` | RGB 颜色线性插值 |
| `calculateAngle(progress)` | 将进度百分比转换为角度 |
| `animateProgress()` | 动画循环函数 |
| `updateProgress(newProgress)` | 更新目标进度并启动动画 |
## 性能优化
1. **分段数量控制**:根据进度动态调整分段数,避免过度绘制
2. **动画帧控制**:使用 `requestAnimationFrame` 优化渲染性能
3. **差值判断**:当接近目标值时直接设置,避免无限循环
## 交互功能
| 功能 | 实现方式 |
|------|---------|
| 滑块控制 | `input[type="range"]` 监听 `input` 事件 |
| 测试按钮 | 预设百分比值快速测试 |
| 动画演示 | 从 0% 到 100% 自动播放 |
| 随机进度 | 生成 0-100 随机值 |
## 技术要点总结
1. **Canvas 坐标系**:默认 0° 在 3 点钟方向,通过 CSS 旋转调整到 12 点钟
2. **角度计算**:使用弧度制,`角度 × Math.PI / 180`
3. **渐变实现**:分段绘制 + 颜色插值,而非 Canvas 原生渐变
4. **文字旋转**:容器旋转后,文字元素需要反向旋转保持可读
5. **动画平滑度**:缓动函数 + requestAnimationFrame 实现流畅动画