
一、应用概述进度环Ring Progress是一种以环形方式展示进度的可视化组件相较于传统的水平进度条进度环具有更紧凑的布局、更美观的视觉效果和更直观的百分比展示能力。本文将以 HarmonyOS ArkTS 框架为基础详细解析一个功能完善的动态进度环组件的开发过程该组件支持百分比显示、颜色渐变、动画过渡和自定义配置等核心功能。1.1 功能特性环形进度显示以圆弧方式直观展示 0% 到 100% 的进度百分比数值显示环中心实时显示当前百分比数值颜色随百分比变化进度环颜色根据进度的不同阶段自动变化如低分为红色中分为橙色高分为绿色平滑动画过渡进度变化时伴随平滑的圆弧增长动画可自定义配置环的尺寸、颜色、宽度、起始角度等均可配置多种颜色模式支持固定颜色、渐变色和分段颜色三种模式1.2 适用场景数据仪表盘展示健康应用步数/卡路里目标追踪课程学习进度存储空间使用情况下载/上传进度展示游戏经验值/等级进度1.3 技术亮点进度环组件的核心在于利用 Canvas 绘制能力和动画系统实现流畅的圆弧动画是探索 ArkTS 图形绘制能力的经典案例。二、技术架构2.1 整体架构┌──────────────────────────────────────────┐ │ RingProgressContainer │ │ 进度环容器组件 │ ├──────────────────────────────────────────┤ │ ┌────────────────────────────────────┐ │ │ │ Canvas 绘制层 │ │ │ │ ┌─────背景圆弧─────┐ │ │ │ │ │ ┌─进度圆弧─┐ │ │ │ │ │ │ │ 中心文字 │ │ │ │ │ │ │ └─────────┘ │ │ │ │ │ └─────────────────┘ │ │ │ └────────────────────────────────────┘ │ │ │ │ ┌────────────────────────────────────┐ │ │ │ 信息展示区 │ │ │ │ 标题 | 当前值/目标值 │ │ │ └────────────────────────────────────┘ │ └──────────────────────────────────────────┘2.2 核心数据结构// 进度环配置接口 interface RingProgressConfig { size: number; // 组件尺寸宽高相同 ringWidth: number; // 环的宽度 backgroundColor: ResourceColor; // 背景圆弧颜色 progressColor: ResourceColor; // 进度圆弧颜色单色模式 useGradient: boolean; // 是否使用渐变色 gradientColors: ResourceColor[]; // 渐变色数组 useSegmentColors: boolean; // 是否使用分段颜色 segmentConfig: SegmentColor[]; // 分段颜色配置 startAngle: number; // 起始角度默认 -90从顶部开始 maxValue: number; // 最大值默认 100 showPercentage: boolean; // 是否显示百分比文字 animationDuration: number; // 动画时长毫秒 roundCap: boolean; // 是否圆角端点 } // 分段颜色配置 interface SegmentColor { threshold: number; // 阈值百分比 color: ResourceColor; // 对应颜色 } // 默认分段颜色配置 const DEFAULT_SEGMENT_CONFIG: SegmentColor[] [ { threshold: 33, color: #FF4444 }, // 0-33%红色 { threshold: 66, color: #FFA500 }, // 33-66%橙色 { threshold: 100, color: #4CAF50 } // 66-100%绿色 ];2.3 组件状态管理State currentProgress: number → 当前进度值驱动动画 State displayPercentage: number → 显示的百分比整数 State isAnimating: boolean → 是否正在动画中 private targetProgress: number → 目标进度值 private animationStartValue: number → 动画起始值 private animationId: number → 动画帧 ID三、核心代码分析3.1 进度环主体组件进度环的核心是使用 Canvas 组件进行圆弧绘制这是 ArkTS 中实现自定义图形的最佳方式。Component struct RingProgress { private config: RingProgressConfig; State currentProgress: number 0; State displayPercentage: number 0; private canvasWidth: number 0; private canvasHeight: number 0; // 获取当前进度对应的颜色 get activeColor(): ResourceColor { if (this.config.useSegmentColors) { const percentage (this.currentProgress / this.config.maxValue) * 100; for (const seg of this.config.segmentConfig) { if (percentage seg.threshold) { return seg.color; } } return this.config.segmentConfig[this.config.segmentConfig.length - 1].color; } if (this.config.useGradient) { // 渐变色模式返回第一个颜色实际渐变在 Canvas 中实现 return this.config.gradientColors[0]; } return this.config.progressColor; } build() { Stack() { // Canvas 绘制区域 Canvas(this.drawRing.bind(this)) .width(this.config.size) .height(this.config.size) .onReady(() { // Canvas 就绪后开始动画 this.animateToProgress(this.targetProgress); }) // 中心文字 if (this.config.showPercentage) { Column({ space: 4 }) { Text(${this.displayPercentage}%) .fontSize(this.config.size * 0.18) .fontWeight(FontWeight.Bold) .fontColor(this.activeColor) .animation({ duration: this.config.animationDuration, curve: Curve.EaseOut }) if (this.config.maxValue ! 100) { Text(${Math.round(this.currentProgress)}/${this.config.maxValue}) .fontSize(this.config.size * 0.08) .fontColor(#999999) } } } } .width(this.config.size) .height(this.config.size) } // Canvas 绘制方法 drawRing(context: CanvasRenderingContext2D) { const width this.config.size; const height this.config.size; const centerX width / 2; const centerY height / 2; const radius (width - this.config.ringWidth) / 2; const startAngle (this.config.startAngle * Math.PI) / 180; // 清空画布 context.clearRect(0, 0, width, height); // 1. 绘制背景圆弧 context.beginPath(); context.arc(centerX, centerY, radius, startAngle, startAngle 2 * Math.PI); context.lineWidth this.config.ringWidth; context.strokeStyle this.config.backgroundColor as string; context.stroke(); // 2. 计算进度圆弧的角度 const progressRatio this.currentProgress / this.config.maxValue; const progressAngle 2 * Math.PI * progressRatio; const endAngle startAngle progressAngle; // 3. 绘制进度圆弧 context.beginPath(); context.arc(centerX, centerY, radius, startAngle, endAngle); context.lineWidth this.config.ringWidth; // 设置端点样式 if (this.config.roundCap) { context.lineCap round; } // 设置颜色支持渐变 if (this.config.useGradient this.config.gradientColors.length 1) { const gradient context.createLinearGradient( centerX - radius, centerY, centerX radius, centerY ); this.config.gradientColors.forEach((color, index) { gradient.addColorStop(index / (this.config.gradientColors.length - 1), color as string); }); context.strokeStyle gradient; } else { context.strokeStyle this.activeColor as string; } context.stroke(); // 4. 绘制端点装饰可选 if (progressRatio 0 this.config.roundCap) { // 在进度终点绘制小圆点 const dotAngle endAngle; const dotX centerX radius * Math.cos(dotAngle); const dotY centerY radius * Math.sin(dotAngle); context.beginPath(); context.arc(dotX, dotY, this.config.ringWidth / 2, 0, 2 * Math.PI); context.fillStyle this.activeColor as string; context.fill(); } } }Canvas 绘制要点角度计算startAngle默认为-90°从正上方开始顺时针旋转。进度角度为2π * progressRatio。双圆弧绘制先绘制灰色背景圆弧再在之上绘制彩色进度圆弧通过 Canvas 的叠加绘制实现多层效果。渐变支持使用createLinearGradient创建线性渐变将渐变应用到进度圆弧上。圆角端点通过lineCap round实现圆角效果同时额外绘制一个小圆点增强视觉效果。3.2 进度动画实现进度变化的动画通过 ArkTS 的动画 API 实现平滑过渡// 方式一使用 animateTo 显式动画 animateToProgress(targetValue: number) { // 限制目标值范围 const clampedTarget Math.max(0, Math.min(targetValue, this.config.maxValue)); animateTo({ duration: this.config.animationDuration, curve: Curve.FastOutSlowIn, onFinish: () { this.isAnimating false; } }, () { // 在动画闭包中修改状态框架自动插值 this.currentProgress clampedTarget; this.displayPercentage Math.round((clampedTarget / this.config.maxValue) * 100); }) } // 方式二使用定时器实现逐帧动画 animateProgressStepByStep(from: number, to: number) { const duration this.config.animationDuration; const steps 60; // 60帧 const stepValue (to - from) / steps; const interval duration / steps; let currentStep 0; const timerId setInterval(() { currentStep; if (currentStep steps) { clearInterval(timerId); this.currentProgress to; this.displayPercentage Math.round((to / this.config.maxValue) * 100); return; } // 使用缓动函数计算当前值 const t currentStep / steps; const easedT this.easeOutCubic(t); const value from (to - from) * easedT; this.currentProgress value; this.displayPercentage Math.round((value / this.config.maxValue) * 100); }, interval); } // 缓动函数 easeOutCubic(t: number): number { return 1 - Math.pow(1 - t, 3); }推荐使用方式一animateTo原因如下框架级别的动画调度性能更优自动处理动画过程中的帧同步支持动画取消和中断代码更简洁易于维护3.3 进度环颜色控制颜色随百分比变化是进度环的核心交互特征// 根据百分比获取对应的分段颜色 getSegmentColor(percentage: number): ResourceColor { // 将分段配置按阈值排序 const sortedConfig [...this.config.segmentConfig] .sort((a, b) a.threshold - b.threshold); for (const seg of sortedConfig) { if (percentage seg.threshold) { return seg.color; } } // 超过最大值使用最后一个颜色 return sortedConfig[sortedConfig.length - 1].color; } // 动态更新颜色 updateColorForProgress(progress: number) { const percentage (progress / this.config.maxValue) * 100; const newColor this.getSegmentColor(percentage); // 颜色变化也伴随动画 animateTo({ duration: 200, curve: Curve.EaseOut }, () { // 颜色属性通过 State 驱动 Canvas 重绘 this.activeColorState newColor; }) }颜色分段设计进度范围颜色心理暗示0% - 33%红色 #FF4444警示、不足、危险33% - 66%橙色 #FFA500提醒、注意、进行中66% - 100%绿色 #4CAF50安全、完成、优秀这套颜色方案符合用户对红-黄-绿的通用认知可以直观地通过颜色判断进度所处的阶段。3.4 使用 Builder 重构子组件Builder RingCanvas(config: RingProgressConfig, progress: number, activeColor: ResourceColor) { Canvas((context: CanvasRenderingContext2D) { const width config.size; const height config.size; const centerX width / 2; const centerY height / 2; const radius (width - config.ringWidth) / 2; const startAngle (config.startAngle * Math.PI) / 180; // 背景圆弧 context.beginPath(); context.arc(centerX, centerY, radius, startAngle, startAngle 2 * Math.PI); context.lineWidth config.ringWidth; context.strokeStyle config.backgroundColor as string; context.stroke(); // 进度圆弧 const progressRatio progress / config.maxValue; const endAngle startAngle (2 * Math.PI) * progressRatio; context.beginPath(); context.arc(centerX, centerY, radius, startAngle, endAngle); context.lineWidth config.ringWidth; context.strokeStyle activeColor as string; if (config.roundCap) context.lineCap round; context.stroke(); }) .width(config.size) .height(config.size) }四、HarmonyOS关键技术应用4.1 Canvas 2D 绘图 APIHarmonyOS 的 Canvas 组件提供了完整的 2D 绘图上下文支持丰富的图形绘制方法核心绘图方法方法说明本应用中的用途beginPath()开始新路径每个圆弧绘制前调用arc(x, y, r, s, e)绘制圆弧绘制环形进度stroke()描边路径渲染圆弧线条fill()填充路径填充中心圆点clearRect()清除矩形区域每帧重绘前清空createLinearGradient()创建线性渐变进度颜色渐变createRadialGradient()创建径向渐变可选的中心发光效果Canvas 绘制优化技巧// 1. 使用 requestAnimationFrame 代替 setInterval animateWithRAF(target: number) { const animate () { // 更新进度值 this.currentProgress (target - this.currentProgress) * 0.1; if (Math.abs(this.currentProgress - target) 0.5) { // 继续动画 requestAnimationFrame(animate); } else { this.currentProgress target; } }; requestAnimationFrame(animate); } // 2. 矩阵变换优化 context.save(); context.translate(centerX, centerY); context.rotate(rotationAngle); // 绘制内容 context.restore(); // 3. 避免频繁的样式设置 // 批量设置样式减少 API 调用 context.strokeStyle color; context.lineWidth width; context.lineCap round; context.beginPath(); // 绘制... context.stroke();4.2 动画系统与 Canvas 的协同进度环组件的动画效果涉及两个层面的协同┌──────────────────────────────┐ │ State 层 │ │ currentProgress 变化触发 │ │ → displayPercentage 更新 │ ├──────────────────────────────┤ │ 动画引擎 │ │ animateTo() 插值计算 │ │ (FastOutSlowIn 曲线) │ ├──────────────────────────────┤ │ Canvas 重绘 │ │ bind(this.drawRing) │ │ 每次 State 变更自动调用 │ └──────────────────────────────┘协同机制animateTo修改State currentProgress的值ArkUI 框架检测到状态变化触发组件重新渲染组件重新执行build()方法Canvas 调用drawRing方法Canvas 根据最新的currentProgress值重新绘制圆弧以上过程每秒执行 60 次形成流畅动画4.3 组件生命周期管理Component struct RingProgress { // ... aboutToAppear() { // 初始化 Canvas 尺寸 this.canvasWidth this.config.size; this.canvasHeight this.config.size; // 如果设置了初始值直接跳转到目标进度无动画 if (this.config.initialProgress 0) { this.currentProgress this.config.initialProgress; this.displayPercentage Math.round( (this.config.initialProgress / this.config.maxValue) * 100 ); } } aboutToDisappear() { // 清除动画 this.isAnimating false; // 清理资源 // Canvas 上下文自动释放 } // 页面显示/隐藏时的处理 onPageShow() { // 页面显示时重新触发动画 if (this.targetProgress 0) { this.animateToProgress(this.targetProgress); } } onPageHide() { // 页面隐藏时暂停动画 this.isAnimating false; } }五、UI设计与交互5.1 视觉设计设计原则简洁直观环形进度 中心百分比文字信息一目了然色彩提示颜色随进度变化提供额外的信息维度动效增强进度变化配合平滑动画提升用户体验尺寸规范组件尺寸200 × 200 px (默认) 环的宽度16 px (默认) 环的半径92 px (默认) 中心文字36 px (约组件尺寸的 18%)配色方案背景环浅灰色#E8E8E8视觉弱化进度环分段模式0-33%红色#FF444433-66%橙色#FFA50066-100%绿色#4CAF50中心文字与进度环当前颜色一致辅助文字灰色#9999995.2 交互反馈进度变化圆弧从起始角度顺时针延伸动画时长 500ms默认颜色过渡当进度跨越分段阈值时颜色平滑过渡非突变数字动画中心百分比数字逐位变化不使用跳变提示信息进度达到 100% 时可触发完成动画如脉冲效果5.3 多状态展示// 空状态0% if (this.currentProgress 0) { // 显示空心环 未开始 提示 } // 进行中 if (this.currentProgress 0 this.currentProgress this.config.maxValue) { // 标准显示 } // 完成100% if (this.currentProgress this.config.maxValue) { // 显示完成特效如脉冲光环 }六、性能优化与最佳实践6.1 Canvas 性能优化// 1. 使用离屏 Canvas 缓存静态元素 private offscreenCanvas: OffscreenCanvas | null null; drawRing(context: CanvasRenderingContext2D) { // 绘制背景环只绘制一次缓存到离屏 Canvas if (!this.offscreenCanvas) { // 创建离屏 Canvas // 绘制背景环到离屏 Canvas } // 将背景环绘制到主 Canvas context.drawImage(this.offscreenCanvas, 0, 0); // 只重新绘制进度环动态部分 this.drawProgressArc(context); } // 2. 限制重绘频率 private lastDrawTime: number 0; private readonly MIN_DRAW_INTERVAL 16; // ~60fps drawRing(context: CanvasRenderingContext2D) { const now Date.now(); if (now - this.lastDrawTime this.MIN_DRAW_INTERVAL) { return; // 跳过此次绘制 } this.lastDrawTime now; // 实际绘制逻辑 } // 3. 使用整数坐标 context.arc( Math.round(centerX), Math.round(centerY), Math.round(radius), startAngle, endAngle );6.2 状态更新优化// 批量更新状态减少触发重绘次数 updateProgress(newProgress: number) { const clampedProgress Math.max(0, Math.min(newProgress, this.config.maxValue)); // 使用 partialUpdate 批量更新 animateTo({ duration: this.config.animationDuration, curve: Curve.EaseOut }, () { // 在一个动画闭包中同时更新多个状态 this.currentProgress clampedProgress; this.displayPercentage Math.round((clampedProgress / this.config.maxValue) * 100); this.activeColor this.getSegmentColor(this.displayPercentage); }) }6.3 最佳实践总结实践说明使用onReady事件确保 Canvas 初始化完成后再绘制避免setInterval优先使用animateTo或requestAnimationFrame颜色缓存将颜色值缓存为状态变量避免重复计算尺寸预设Canvas 尺寸在aboutToAppear中预设避免动态计算清理资源在aboutToDisappear中清理动画和定时器七、总结与扩展思路7.1 项目总结本文详细解析了基于 HarmonyOS ArkTS 框架的进度环组件开发核心内容包括Canvas 2D 绘图掌握圆弧绘制、路径管理、渐变填充等绘图技术动画系统使用animateTo实现状态驱动的平滑动画颜色管理分段颜色和渐变颜色的实现策略组件封装将进度环封装为可配置的自定义组件性能优化Canvas 绘制优化和状态更新优化7.2 扩展思路功能增强多环叠加多个进度环叠加显示如总进度 子进度仪表盘风格将环形改为半圆或扇形模拟汽车仪表盘自定义刻度在环上增加刻度标记数据标注在进度环上标注关键节点如目标值标记交互升级点击交互点击进度环显示详细信息拖拽调节用户可通过拖拽圆弧调整进度值手势缩放双指缩放查看详情进度预览悬停显示具体数值技术进阶3D 进度环使用 3D 变换实现立体效果粒子特效进度完成时触发粒子庆祝效果分布式同步多设备同步显示同一进度数据数据绑定与后端数据源绑定实时更新进度7.3 设计启示进度环组件的设计思想——用圆形替代线性来展示进度——本身就体现了良好的空间利用率和视觉美感。在实际项目中可以将这种环形可视化的思路延伸到更多场景环形进度 → 环形菜单 → 环形图表 → 环形导航 ↓ ↓ ↓ ↓ 进度展示 快捷操作 数据统计 空间导航圆形作为一种完美的几何形状在 UI 设计中的应用远不止于此。掌握 Canvas 绘图能力后开发者可以创造出更多富有创意的圆形交互组件。项目代码已完整开源。进度环组件是探索 ArkTS Canvas 绘图能力的绝佳入口掌握其开发方法将为构建复杂的数据可视化组件奠定坚实基础。