写点什么

Canvas 绘制进度条

作者:cfx
  • 2025-05-13
    北京
  • 本文字数:5052 字

    阅读完需:约 17 分钟

Canvas 绘制进度条

用 Canvas 绘制的灵活性更强大的进度条组件

在现代应用开发中,进度条是一个常见且实用的组件,用于向用户直观展示任务的进展情况。鸿蒙目前虽然已经有了 Progress 组件,但是该组件使用起来有较大局限性,例如将进度条和等级、图片、时间绑定时则无法匹配,类似快递员进度,外卖进度,游戏等级进度,往往伴随着时间,图标等实时的进度标识,Progress,则无法有效的实现该功能,本文将介绍一种使用 Canvas 绘制的进度条组件,相较于传统方式,它具有更强的灵活性与可定制性。


一、组件原理剖析

(一)基本参数设定

  1. 进度数值相关:通过@Param currentValue设置当前进度值,默认设为 3000 ,@Param maxValue设置最大进度值,默认 8000 。这两个参数的配合,能精准反映任务完成的比例。

  2. 奖励节点设定@Param rewardPoints是一个数组,存储着如 2900、4000 等奖励节点数值。当进度达到这些节点时,可触发相应奖励机制。与之关联的@Param rewardImages数组,用于存放每个奖励节点对应的图片资源(若未提供,则使用@Param defaultImage指定的默认图片) 。

  3. 样式参数@Param progressColor指定进度条有值部分的颜色,默认是橙色(#FF8C00) ;@Param progressBackgroundColor设定进度条无值部分的颜色,默认浅灰色(#E0E0E0) 。还有如progressWidth(进度条实际宽度,默认 400)、progressHeight(进度条高度,默认 7)等参数,用于控制进度条的尺寸。

(二)Canvas 绘图逻辑

  1. 绘制圆角矩形:利用drawRoundRect方法绘制圆角矩形。通过传入起始坐标(x、y)、矩形宽高(width、height)和圆角半径(radius) ,在 Canvas 上下文context中,按照顺时针顺序,依次绘制矩形的四条边和四个圆角,精确构建进度条的外观。

  2. 绘制进度条:在drawProgress方法中,首先清空画布。接着,用progressBackgroundColor填充背景条,再根据当前进度值与最大进度值的比例,使用progressColor绘制进度条部分,实现进度可视化。

(三)奖励图标与数值展示

DrawProgressBar构建方法中,通过ForEach循环遍历奖励节点数组rewardPoints。对于每个节点,先根据是否有对应的奖励图片资源,选择显示相应图片(并依据当前进度设置透明度) ,然后在图片下方显示节点数值(根据进度设置颜色) ,并通过精确的位置计算,确保图标和数值在进度条上的合适位置展示。

二、实际应用场景

(一)任务进度展示

在项目管理应用中,可用于展示任务的完成进度。例如,一个软件开发项目,将不同阶段设置为奖励节点,当开发人员完成相应阶段任务时,进度条推进,同时解锁对应奖励图标,激励开发团队。

(二)学习进度跟踪

在教育类应用里,能跟踪学生的学习进度。比如,将课程章节学习、作业完成等设置为奖励节点,学生每完成一个学习目标,进度条前进,显示学习成果,激发学习动力。

(三)游戏礼包,金币,福利进度

(游戏中与礼包领取进度、金币积累进度、福利获取进度等相关应用)

游戏加载进度展示

在游戏启动、关卡切换或资源预加载时,使用 Canvas 进度条能直观呈现加载进度。比如大型 3D 游戏,启动需加载大量模型、纹理等资源,进度条可告知玩家加载状态,避免等待焦虑。像开放世界游戏,进入新区域时的加载界面,进度条就很实用。

关卡进度追踪(重点应用场景)

用于显示玩家在游戏关卡内的进度,如探索地图的比例、完成任务的阶段等。假设玩家在一个广阔区域冒险,进度条可显示区域探索完成度,激励玩家探索全部内容 。

技能冷却计时

游戏中角色释放技能后有冷却时间,进度条可直观展示冷却剩余时长。魔兽类游戏里,英雄技能冷却时,进度条能帮助玩家把握技能释放时机,提升操作精准度。

经验值或任务进度显示(重点应用场景)

玩家积累经验值升级,或完成系列任务时,进度条可展示当前进度。例如游戏中,玩家打怪、做任务获取经验值,进度条显示离下一级还差多少,让玩家明确目标 。

游戏内活动进度(重点应用场景)

举办限时活动时,进度条展示活动任务完成进度,如收集特定道具数量、完成指定挑战次数等。刺激玩家参与活动,提升游戏活跃度

三、优势突显

对比原生 Progress

样式定制方面

  • 形状灵活性:鸿蒙Progress组件一般是常规的线性或环形样式 。而自定义的 Canvas 进度条,借助drawRoundRect等绘图方法,能轻松绘制出圆角矩形等特殊形状的进度条,甚至可根据游戏美术风格绘制异形进度条,样式更为丰富。

  • 颜色与图案:虽然鸿蒙Progress也能设置颜色等样式,但 Canvas 进度条在颜色设置上更具自由度,可通过代码灵活实现渐变色填充等效果;还能结合游戏主题,在进度条上绘制独特图案,如游戏内的魔法符文等元素 。

功能拓展方面

  • 奖励机制融合:游戏中常需要奖励机制激励玩家,自定义 Canvas 进度条可方便地结合rewardPoints(奖励节点)、rewardImages(奖励图片)等参数,在进度达到相应节点时展示奖励图标等,而鸿蒙原生Progress组件没有这类直接集成的奖励关联功能。

  • 动态交互效果:利用 Canvas 的特性,可实现更多动态交互效果,比如进度条在推进过程中,奖励图标逐渐清晰显示等动画效果。相比之下,鸿蒙Progress组件原生的交互效果较为有限 。

性能与适配方面

  • 轻量级绘制:在一些对性能要求高的游戏场景中,Canvas 绘制可按需精准控制渲染区域和内容,减少不必要的资源消耗。例如在手机端游戏中,可根据屏幕尺寸灵活调整进度条绘制,避免过度渲染。

  • 多场景适配:对于游戏内各种复杂场景,如不同分辨率的游戏界面、特殊视角的 UI 布局等,自定义 Canvas 进度条能通过代码更灵活地进行适配调整,适配性更强。

(一)高度可定制

开发者可自由设置进度条的颜色、尺寸,以及奖励节点和对应图片等参数,轻松适配不同应用的 UI 风格和业务需求。

(二)视觉效果灵活

借助 Canvas 强大的绘图能力,能实现如圆角矩形等复杂的进度条样式,还可灵活控制奖励图标和数值的展示效果,提升用户体验。

(三)性能表现良好

Canvas 绘图在处理图形渲染时效率较高,能够保证进度条在不同设备和场景下,都能流畅显示,不会过多占用系统资源。这种基于 Canvas 绘制的进度条组件,凭借其灵活的参数设置、独特的绘图逻辑和丰富的展示效果,在各类应用开发中具有广阔的应用前景,能为用户带来更直观、有趣的交互体验。

//图片资源需进行替换@Entry@ComponentV2export struct CustomProgressBar {  // 当前进度值,默认3000  @Param currentValue: number = 3000  // 最大进度值,默认8000  @Param maxValue: number = 8000  // 奖励节点数组,每个节点代表一个奖励点  @Param rewardPoints: number[] = [2900, 4000, 4500, 6000, 8000]  // 奖励图片资源数组,可选,每个节点对应一个图片  @Param rewardImages?: Array<ResourceStr> =    [$r('app.media.ic_love'), $r('app.media.ic_love'), $r('app.media.ic_love'), $r('app.media.ic_love'),]  // 默认奖励图片,当rewardImages未提供时使用  @Param defaultImage?: ResourceStr = $r('app.media.startIcon')  // 进度条有值部分的颜色,默认橙色  @Param progressColor: string = '#FF8C00'  // 进度条无值部分的颜色,默认浅灰色  @Param progressBackgroundColor: string = '#E0E0E0'  // 进度条实际宽度  private progressWidth: number = 400  // 进度条高度  private progressHeight: number = 7  // 奖励图标大小  private iconSize: number = 24  // Canvas上下文  private context: CanvasRenderingContext2D | null = null  // 容器宽度,用于控制可视区域  @Param containerWidth: number = 300  // 奖励图标的宽度,默认24  @Param iconWidth: number = 24  // 奖励图标的高度,默认24  @Param iconHeight: number = 24  // 默认图标的宽度,默认24  @Param defaultIconWidth: number = 24  // 默认图标的高度,默认24  @Param defaultIconHeight: number = 24
// 绘制圆角矩形的辅助方法 // x, y: 起始坐标 // width, height: 矩形宽高 // radius: 圆角半径 private drawRoundRect(x: number, y: number, width: number, height: number, radius: number) { if (this.context) { this.context.beginPath() // 从左上角开始,顺时针绘制 this.context.moveTo(x + radius, y) // 左上角圆弧起点 this.context.lineTo(x + width - radius, y) // 上边 this.context.arcTo(x + width, y, x + width, y + radius, radius) // 右上角圆弧 this.context.lineTo(x + width, y + height - radius) // 右边 this.context.arcTo(x + width, y + height, x + width - radius, y + height, radius) // 右下角圆弧 this.context.lineTo(x + radius, y + height) // 下边 this.context.arcTo(x, y + height, x, y + height - radius, radius) // 左下角圆弧 this.context.lineTo(x, y + radius) // 左边 this.context.arcTo(x, y, x + radius, y, radius) // 左上角圆弧 this.context.closePath() } }
@Builder DrawProgressBar() { Scroll() { Stack() { // 进度条画布,位于底层 Canvas(this.context) .width(this.progressWidth) .height(this.progressHeight) .position({ x: 0, y: Math.max(this.iconHeight, this.defaultIconHeight) / 2 }) .onReady(() => { this.drawProgress() })
// 奖励图标和数值,位于上层 ForEach(this.rewardPoints, (point: number, index) => { Column() { // 显示奖励图标,根据进度设置透明度 if (this.rewardImages?.[index]) { Image(this.rewardImages[index]) .width(this.iconWidth) .height(this.iconHeight) .opacity(this.currentValue >= point ? 1 : 0.5) } else if (this.defaultImage) { Image(this.defaultImage) .width(this.defaultIconWidth) .height(this.defaultIconHeight) .opacity(this.currentValue >= point ? 1 : 0.5) } // 显示节点数值,根据进度设置颜色 Text(point.toString()) .fontSize(11) .fontColor(this.currentValue >= point ? '#FF8C00' : '#8E8E8E') .margin({ top: 2 }) } .position({ x: (point / this.maxValue) * this.progressWidth - Math.max(this.iconWidth, this.defaultIconWidth) / 2, y: 0 }) .alignItems(HorizontalAlign.Center) }) } .width(this.progressWidth + Math.max(this.iconWidth, this.defaultIconWidth)) // 增加宽度以确保最后一个节点完全显示 .height(Math.max(this.iconHeight, this.defaultIconHeight) + this.progressHeight + 15) .padding({ right: Math.max(this.iconWidth, this.defaultIconWidth) }) // 添加右侧内边距 } .scrollable(ScrollDirection.Horizontal) .scrollBar(BarState.Off) .width(this.containerWidth) }
// 绘制进度条 private drawProgress() { if (this.context) { // 清空画布 this.context.clearRect(0, 0, this.progressWidth, this.progressHeight)
// 绘制背景条(使用backgroundColor) this.context.fillStyle = this.progressBackgroundColor this.drawRoundRect(0, 0, this.progressWidth, this.progressHeight, this.progressHeight / 2) this.context.fill()
// 绘制进度条(使用progressColor) const progress = Math.min(this.currentValue / this.maxValue, 1) this.context.fillStyle = this.progressColor this.drawRoundRect(0, 0, this.progressWidth * progress, this.progressHeight, this.progressHeight / 2) this.context.fill() } }
// 组件初始化时创建Canvas上下文 aboutToAppear() { this.context = new CanvasRenderingContext2D() }
// 构建组件 build() { Column() { this.DrawProgressBar() } .width('100%') .alignItems(HorizontalAlign.Center) }}
复制代码


用户头像

cfx

关注

还未添加个人签名 2025-05-06 加入

还未添加个人简介

评论

发布
暂无评论
Canvas 绘制进度条_鸿蒙_cfx_InfoQ写作社区