HarmonyOS Canvas 绘图实战:从静态圆到动态交互
本文介绍 HarmonyOS 中 Canvas 组件的基础用法,涵盖 CanvasRenderingContext2D 接口详解、绘制实心圆与描边步骤、利用 @State 实现动态圆环响应式更新、触摸事件交互处理,以及渐变填充和性能优化建议。提供适配 API 6.0.2 的完整 ArkTS 代码示例。

本文介绍 HarmonyOS 中 Canvas 组件的基础用法,涵盖 CanvasRenderingContext2D 接口详解、绘制实心圆与描边步骤、利用 @State 实现动态圆环响应式更新、触摸事件交互处理,以及渐变填充和性能优化建议。提供适配 API 6.0.2 的完整 ArkTS 代码示例。

在 HarmonyOS 生态中,Canvas 是实现自定义图形绘制的核心组件。无论是游戏开发、数据可视化,还是 UI 动效,掌握 Canvas 的基本绘图 API 都是每位开发者必须具备的能力。
本文将带你从零开始,深入理解 HarmonyOS 6.0.0(API 6.0.2) 中的 Canvas 组件,学习如何使用 CanvasRenderingContext2D 接口绘制一个简单的圆,并逐步扩展到动态圆环、渐变填充等高级技巧。
我们将基于 OpenHarmony 6.0 的官方规范,确保代码完全兼容于你提供的虚拟机环境(HarmonyOS 6.0.0, API 6.0.2(22)),并提供完整的可运行示例。
Canvas 是 HarmonyOS 提供的一个轻量级绘图容器,允许开发者通过 JavaScript/ArkTS 代码直接操作图形上下文,实现自定义图形渲染。
它类似于 HTML5 的 <canvas> 元素,但在 HarmonyOS 中被封装为一个声明式组件,支持以下功能:
| 功能 | 描述 |
|---|---|
| 基础绘图 | 线条、圆形、矩形、路径等 |
| 颜色与样式 | 填充、描边、透明度、渐变 |
| 变换 | 平移、旋转、缩放 |
| 图像处理 | 绘制图片、裁剪、合成 |
✅ 适用场景:游戏角色动画、数据图表(柱状图、饼图)、手写签名、动态进度条、自定义控件
在 HarmonyOS 的 UI 框架中,Canvas 属于 UI 组件层,位于 @ohos.arkui 模块下。它不依赖任何第三方库,是系统原生支持的绘图能力。
// 导入 Canvas 组件(无需额外依赖)
import { Canvas } from '@ohos.arkui';
📌 注意:在 DevEco Studio 4.1+ 中,
Canvas已默认包含,无需手动添加依赖。
CanvasRenderingContext2D 是 Canvas 的绘图上下文接口,它提供了所有绘图方法和属性。
当你在 Canvas 中使用 (ctx: CanvasRenderingContext2D) => { ... } 回调时,ctx 就是一个 CanvasRenderingContext2D 实例。
Canvas((ctx: CanvasRenderingContext2D) => {
// 所有绘图操作都在这里执行
})
🔍 核心作用:控制画笔颜色、粗细;定义路径(path);填充或描边;应用变换(transform)
| 属性/方法 | 说明 |
|---|---|
beginPath() | 开始一条新路径 |
arc(x, y, r, startAngle, endAngle) | 绘制圆弧 |
fill() | 填充当前路径 |
stroke() | 描边当前路径 |
fillStyle | 设置填充颜色 |
strokeStyle | 设置描边颜色 |
lineWidth | 设置线条宽度 |
closePath() | 关闭路径(连接起点和终点) |
这些方法在 OpenHarmony 6.0 中已全面支持,且性能优化良好。
我们从最简单的任务开始:画一个红色实心圆,外加白色描边。
Canvas((ctx: CanvasRenderingContext2D) => {
// 绘图逻辑
})
代码讲解:
Canvas是一个函数式组件,接收一个回调函数;回调参数ctx是CanvasRenderingContext2D实例;所有绘图操作都通过ctx调用。
ctx.beginPath();
代码讲解:
beginPath()表示'我要开始画一条新路径了';每次绘制新图形前,必须调用此方法;否则会继续上一条路径,导致图形粘连。
ctx.arc(100, 100, 50, 0, 2 * Math.PI);
参数说明:
(100, 100):圆心位于屏幕左上角 100px 处50:半径为 50px0到2 * Math.PI:表示绘制一个完整的圆(360°)Math.PI是 π ≈ 3.14159,2 * Math.PI= 6.28318 ≈ 360°
ctx.fillStyle = 'red';
代码讲解:
fillStyle设置路径的填充颜色;支持颜色字符串(如'red','#FF0000','rgb(255,0,0)');不设置时默认为黑色。
ctx.fill();
代码讲解:
fill()将当前路径内部区域用fillStyle指定的颜色填充;此时圆变为红色实心圆。
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
代码讲解:
strokeStyle设置描边颜色;lineWidth设置描边宽度(单位:px);默认描边宽度为 1px。
ctx.stroke();
代码讲解:
stroke()用strokeStyle和lineWidth绘制路径轮廓;此时圆外围出现白色边框。
Canvas((ctx: CanvasRenderingContext2D) => {
ctx.beginPath();
ctx.arc(100, 100, 50, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
ctx.stroke();
});
💡 效果:一个位于 (100,100)、半径 50px 的红色实心圆,带有 2px 白色描边。
静态图形虽然简单,但无法满足交互需求。我们可以通过 @State 或 @Prop 实现动态更新。
@State x: number = 100;
@State y: number = 100;
代码讲解:
@State是 ArkTS 的响应式装饰器;当x或y变化时,UI 会自动重绘;适用于实时动画、用户交互等场景。
Canvas((ctx: CanvasRenderingContext2D) => {
ctx.beginPath();
ctx.arc(this.x, this.y, 50, 0, 2 * Math.PI);
ctx.fillStyle = 'blue';
ctx.fill();
ctx.strokeStyle = 'yellow';
ctx.lineWidth = 3;
ctx.stroke();
});
代码讲解:使用
this.x和this.y替代固定值;当x或y更新时,Canvas会重新执行回调;实现'圆随手指移动'的效果。
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Move) {
this.x = event.touches[0].clientX;
this.y = event.touches[0].clientY;
}
})
代码讲解:
onTouch监听触摸事件;TouchType.Move表示拖动;clientX/Y是触摸点相对于视图的坐标;更新x和y触发重绘。
@Entry
@Component
struct CirclePage {
@State circleX: number = 150;
@State circleY: number = 300;
build() {
// 根容器:Column
Column() {
// 用一个 Stack 来承载圆,确保它能被正确渲染
Stack() {
// 直接用 Circle 组件,比 Shape 更稳定
Circle({ width: 100, height: 100 })
.fill(Color.Blue)
.stroke(Color.Yellow)
.strokeWidth(3)
.position({ x: this.circleX, y: this.circleY })
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor(Color.White)
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down || event.type === TouchType.Move) {
// 减去圆半径,让触摸点在圆中心
this.circleX = event.touches[0].windowX - 50;
this.circleY = event.touches[0].windowY - 50;
}
})
}
}

✅ 效果:点击并拖动屏幕,蓝色圆随手指移动。
const gradient = ctx.createLinearGradient(100, 100, 200, 200);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient;
代码讲解:
createLinearGradient(x1,y1,x2,y2):定义渐变方向;addColorStop(0, 'red'):起始点为红色;addColorStop(1, 'blue'):结束点为蓝色;fillStyle = gradient:应用渐变。
// 绘制背景矩形
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, 300, 300);
// 绘制圆
ctx.beginPath();
ctx.arc(150, 150, 50, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.stroke();
代码讲解:
fillRect(x,y,width,height):绘制矩形;先画背景,再画圆,避免覆盖;fillRect不需要beginPath(),因为它不是路径。
| 优化项 | 建议 |
|---|---|
| 避免频繁重绘 | 使用 @State 控制更新频率 |
| 减少路径复杂度 | 复杂图形分段绘制 |
| 使用离屏缓冲 | 对静态内容使用 Image 替代 Canvas |
| 避免过度变换 | 过度 translate/rotate 会导致性能下降 |
@Entry
@Component
struct CanvasDemo {
@State x: number = 150;
@State y: number = 300;
build() {
Column() {
Stack() {
// 灰色背景
Row()
.width('100%')
.height('100%')
.backgroundColor('#f0f0f0')
// 红色圆 + 文字
Column() {
Text('Drag me!')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Black)
Circle({ width: 100, height: 100 })
.fill(Color.Red)
.stroke(Color.Yellow)
// 把描边改成黄色
.strokeWidth(3)
}.position({ x: this.x - 50, y: this.y - 50 })
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Move) {
this.x = event.touches[0].windowX;
this.y = event.touches[0].windowY;
}
})
}
}

本文详细介绍了 HarmonyOS 6.0.0(API 6.0.2)中 Canvas 的基础绘图 API,包括:
Canvas 组件的使用方式;CanvasRenderingContext2D 的核心方法;arc, fill, stroke);
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online