HarmonyOS 6.0 进度条与环形进度
进度条看起来简单,但HarmonyOS的Progress组件有五种类型,每种适用场景不同。选错了类型体验就很怪——比如用Linear做圆形仪表盘,或者用Ring做横向下载进度。这篇把五种类型全过一遍,再讲自定义环形进度的Canvas实现。
Progress组件五种类型

Progress({ value: 50, total: 100, type: ProgressType.Linear })
| 类型 | 外观 | 典型场景 |
|---|---|---|
| Linear | 横向矩形条 | 下载进度、表单完成度 |
| Ring | 圆环 | 百分比仪表盘、加载等待 |
| Eclipse | 圆形遮罩裁剪 | 图片加载、闪屏过渡 |
| ScaleRing | 带刻度圆环 | 速度表、功率仪表 |
| Capsule | 胶囊形(圆角矩形) | 步数进度、目标达成 |
value是当前值,total是总值。value/total就是完成比例。
Linear——线性进度条
Progress({ value: 75, total: 100, type: ProgressType.Linear })
.width('100%')
.height(8)
.color('#1a73e8')
.backgroundColor('#e0e0e0')
.borderRadius(4)
最基础的横向进度条。height控制粗细,8vp是常见值。borderRadius让两端圆滑。color是进度颜色,backgroundColor是轨道颜色。
Linear适合所有"从左到右推进"的场景——下载、上传、表单填写、测试执行。不够酷但最实用。
Ring——圆环进度
Progress({ value: 75, total: 100, type: ProgressType.Ring })
.width(160)
.height(160)
.color('#1a73e8')
.backgroundColor('#e0e0e0')
.style({ strokeWidth: 12 })
Ring是空心圆环。strokeWidth控制环的粗细。圆环中间是空的,通常在中间放一个Text显示百分比:
Stack() {
Progress({ value: this.progress, total: 100, type: ProgressType.Ring })
.width(160)
.height(160)
.color('#1a73e8')
.style({ strokeWidth: 12 })
Text(this.progress.toString() + '%')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
}
Stack叠加Progress和Text,Text在中间显示百分比。这是最常见的仪表盘模式。
Ring的起始点是12点方向,顺时针推进。这个方向不能改。
Eclipse——遮罩裁剪进度
Progress({ value: 50, total: 100, type: ProgressType.Eclipse })
.width(80)
.height(80)
.color('#1a73e8')
Eclipse是圆形遮罩效果——一个圆从左到右被"揭露"出来。不是环形而是实心圆,未完成部分被遮罩。
这个类型用得最少,主要做图片加载过渡或者闪屏动画。不显示具体数值,只是视觉上"逐渐露出"的效果。
ScaleRing——刻度圆环
Progress({ value: 60, total: 100, type: ProgressType.ScaleRing })
.width(120)
.height(120)
.color('#ff9800')
.backgroundColor('#f5f5f5')
.style({ strokeWidth: 16, scaleCount: 12, scaleWidth: 2 })
ScaleRing在Ring基础上加了刻度线:
- scaleCount:刻度数量(12表示每30度一个刻度)
- scaleWidth:刻度线宽度
- strokeWidth:环粗细
仪表盘风格,适合速度表、温度计等需要刻度感的场景。
Capsule——胶囊进度
Progress({ value: 7000, total: 10000, type: ProgressType.Capsule })
.width('100%')
.height(24)
.color('#43a047')
Capsule是圆角矩形进度条,两端都是半圆。比Linear更圆润,适合步数进度、目标达成这种"可爱"场景。
24vp高度时胶囊效果最明显。高度太低看起来跟Linear差不多。
style参数
style是Progress的细调参数,不同类型支持不同的style字段:
| 参数 | 类型 | 适用 | 说明 |
|---|---|---|---|
| strokeWidth | Length | Ring/ScaleRing | 环宽度 |
| scaleCount | number | ScaleRing | 刻度数量 |
| scaleWidth | Length | ScaleRing | 刻度线宽度 |
| enableSmoothEffect | boolean | 所有 | 是否启用平滑过渡 |
enableSmoothEffect=true时value变化有过渡动画,false时直接跳到新值。默认true。
动态进度更新
用setInterval模拟进度增长:
@State progress: number = 0
private timer: number = -1
startProgress(): void {
this.progress = 0;
this.timer = setInterval(() => {
if (this.progress < 100) {
this.progress += 1;
} else {
clearInterval(this.timer);
}
}, 30);
}
aboutToDisappear(): void {
if (this.timer !== -1) {
clearInterval(this.timer);
}
}
每30ms增长1%,100%时停止。30ms * 100 = 3秒完成。clearInterval在aboutToDisappear中清理,避免内存泄漏。
自定义环形进度——Canvas实现
Progress组件的Ring样式有限——不能调起始角度、不能画渐变色、不能加刻度文字。需要自定义时用Canvas:
private drawRingProgress(value: number, total: number): void {
let ctx: CanvasRenderingContext2D = this.context;
let centerX: number = 100;
let centerY: number = 100;
let radius: number = 70;
let lineWidth: number = 12;
ctx.clearRect(0, 0, 200, 200);
// 背景环
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
ctx.strokeStyle = '#e0e0e0';
ctx.lineWidth = lineWidth;
ctx.lineCap = 'round';
ctx.stroke();
// 进度环
let ratio: number = value / total;
let startAngle: number = -Math.PI / 2;
let endAngle: number = startAngle + ratio * 2 * Math.PI;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.strokeStyle = '#1a73e8';
ctx.lineWidth = lineWidth;
ctx.lineCap = 'round';
ctx.stroke();
}
lineCap='round’让进度端点圆滑。startAngle=-PI/2从12点方向开始。跟Progress的Ring效果一样,但可以自由定制。
渐变色环形进度
// 用createLinearGradient创建渐变
let gradient: CanvasGradient = ctx.createLinearGradient(
centerX - radius, centerY,
centerX + radius, centerY
);
gradient.addColorStop(0, '#1a73e8');
gradient.addColorStop(1, '#43a047');
ctx.strokeStyle = gradient;
createLinearGradient创建水平渐变,起点蓝色到终点绿色。addColorStop的offset从0到1。
渐变方向是固定的linearGradient——即使圆环转了一圈,渐变还是从左到右。要实现"跟随角度"的渐变需要分段绘制。
多段进度
一个环显示多种数据占比:
let segments: number[] = [30, 20, 25, 25];
let colors: string[] = ['#1a73e8', '#43a047', '#ff9800', '#e53935'];
let angle: number = -Math.PI / 2;
for (let i: number = 0; i < segments.length; i++) {
let sweep: number = (segments[i] / 100) * 2 * Math.PI;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, angle, angle + sweep);
ctx.strokeStyle = colors[i];
ctx.lineWidth = lineWidth;
ctx.stroke();
angle += sweep;
}
每段画一段弧,不同颜色。段之间没有间隔。要加间隔的话在angle上加一个小偏移。
确定态vs不确定态
进度有两种状态:
- 确定态:知道具体进度(下载50%),显示具体value
- 不确定态:不知道进度(网络请求中),value设0或用LoadingProgress
// 不确定态
LoadingProgress()
.width(48)
.height(48)
.color('#1a73e8')
LoadingProgress是专门的等待指示器,显示持续旋转动画。没有具体的进度值,纯粹告诉用户"在加载中"。
踩坑清单
| 问题 | 原因 | 解决 |
|---|---|---|
| Ring中间没文字 | Progress不支持内部文字 | 用Stack叠加Text |
| Eclipse看不出进度 | value太小或total太大 | 调整value/total比例 |
| Capsule跟Linear一样 | height太小 | height>=20vp |
| 进度跳变不平滑 | enableSmoothEffect=false | 默认true,不要关 |
| Canvas环形起点不在顶部 | 起始角度0在3点方向 | 用-PI/2从12点开始 |
| setInterval泄漏 | aboutToDisappear没清理 | 加clearInterval |
| 渐变方向不对 | createLinearGradient坐标固定 | 分段绘制或用conicGradient |
| ScaleRing刻度不清晰 | scaleCount太多或strokeWidth太小 | scaleCount<=20, strokeWidth>=8 |
| Progress颜色不支持渐变 | color只接受ResourceColor | 用Canvas自定义实现 |
| value超过total | 进度超过100% | clamp value <= total |
进度条选择很简单:横着的用Linear,圆的用Ring,酷的用Capsule,要刻度用ScaleRing,不够用Canvas。别想太复杂。
更多推荐
所有评论(0)