HarmonyOS7 Canvas 从零开始怎么画:CanvasDrawingStarter 入门实战
文章目录



前言
很多人第一次接触 Canvas,都会有点不适应。平时写 ArkUI,大多数时候都是在拼组件、调布局、改样式;到了 Canvas 这里,突然变成了坐标、上下文、绘制命令,脑回路得整个切一遍。
但你只要迈过第一步,会发现这东西非常值。图形面板、签名板、数据可视化、小游戏、海报生成,背后都离不开绘制能力。问题不是要不要学,而是怎么别把起手式学乱。
这篇就盯住最基础的一件事:在 HarmonyOS7 里,怎么把 Canvas 跑起来,并在准备就绪后画出第一批内容。
这个案例在讲什么
案例很克制,只画了三个彩色矩形,没有上来搞复杂效果。这样的切入是对的,因为 Canvas 最重要的不是“画什么”,而是你得先理解:
- 如何创建渲染上下文
- 如何把上下文绑定给
Canvas - 为什么绘制逻辑要放在
onReady()里
这三步走通,后面的图形、文字、渐变、动画都只是继续往下加能力。
完整代码
import { DEMO_BG_COLOR, DEMO_CARD_COLOR, DEMO_THEME_COLOR, DEMO_SUBTEXT_COLOR } from './types'
@Entry
@Component
struct CanvasDrawingStarter {
@State isShow: boolean = true
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
build() {
Column() {
if (this.isShow) {
Scroll() {
Column({ space: 16 }) {
Text('Canvas 基础绘制').fontSize(16).fontColor(DEMO_THEME_COLOR).fontWeight(FontWeight.Bold)
Column() {
Text('Canvas 组件 (onReady 初始化)').fontSize(13).fontColor('#4D96FF').margin({ bottom: 8 })
Column() {
Canvas(this.context)
.onReady(() => {
this.context.fillStyle = '#4D96FF'
this.context.fillRect(20, 20, 100, 60)
this.context.fillStyle = '#FFA500'
this.context.fillRect(140, 20, 100, 60)
this.context.fillStyle = '#6BCB77'
this.context.fillRect(260, 20, 100, 60)
})
.width('100%')
.height(100)
.backgroundColor('#F0F0F0')
.borderRadius(8)
}
Text('Canvas 使用 onReady 回调初始化绘制').fontSize(11).fontColor(DEMO_SUBTEXT_COLOR).margin({ top: 4 })
}
.backgroundColor(DEMO_CARD_COLOR)
.padding(12)
.borderRadius(12)
Column({ space: 6 }) {
Text('Canvas 绘制基础').fontSize(14).fontColor(DEMO_THEME_COLOR).fontWeight(FontWeight.Bold)
Text('1. RenderingContextSettings 创建设置').fontSize(12).fontColor('#333333')
Text('2. CanvasRenderingContext2D 创建上下文').fontSize(12).fontColor('#333333')
Text('3. Canvas(context) 绑定上下文').fontSize(12).fontColor('#333333')
Text('4. onReady() 回调中执行绘制代码').fontSize(12).fontColor('#333333')
Text('5. 使用 fillRect/strokeRect 等 API 绘制').fontSize(12).fontColor('#333333')
}
.width('100%')
.backgroundColor('#E8F4FD')
.padding(14)
.borderRadius(12)
Column({ space: 6 }) {
Text('Canvas 坐标系').fontSize(14).fontColor(DEMO_THEME_COLOR).fontWeight(FontWeight.Bold)
Text('原点 (0, 0) 在左上角').fontSize(12).fontColor('#333333')
Text('x 轴正方向: 向右').fontSize(12).fontColor('#333333')
Text('y 轴正方向: 向下').fontSize(12).fontColor('#333333')
Text('坐标单位: 像素 px').fontSize(12).fontColor('#333333')
}
.width('100%')
.backgroundColor('#F0F0FF')
.padding(14)
.borderRadius(12)
}
.width('100%')
}
}
Text('CanvasDrawingStarter - onReady 初始化绘图')
.fontSize(12)
.fontColor(DEMO_SUBTEXT_COLOR)
.margin({ top: 12 })
}
.width('100%')
.height('100%')
.backgroundColor(DEMO_BG_COLOR)
.padding(16)
}
}
第一步:先把上下文创建出来
Canvas 不是你扔一个组件上去就能直接画的,它需要一支“笔”。这支笔就是 CanvasRenderingContext2D。
案例里的定义是:
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
这两行代码的关系很简单。
第一行是创建渲染设置,第二行是基于设置生成 2D 上下文。之后你所有画矩形、画线、写文字、加渐变的操作,都是通过这个 context 完成的。
如果你之前用过前端 Canvas,这个模型会很熟悉。HarmonyOS7 这里本质上也是同样思路。
第二步:把上下文绑定给 Canvas
有了上下文之后,还得把它交给组件本身:
Canvas(this.context)
这一步很重要,因为它决定了绘制命令最终落在哪个画布上。可以把它理解成“这支笔要在哪张纸上作画”。
没有这个绑定,你后面的绘图调用就没有明确目标。
第三步:为什么绘制逻辑一定放在 onReady() 里
这是整个入门里最容易忽略,但又最关键的一点。
案例这样写:
Canvas(this.context)
.onReady(() => {
this.context.fillStyle = '#4D96FF'
this.context.fillRect(20, 20, 100, 60)
})
为什么不直接在 build() 里画?因为 Canvas 还没真正准备好时,绘制上下文对应的实际画布区域可能还没完成初始化。你过早去画,结果要么没显示,要么行为不稳定。
onReady() 的意思很明确:画布就绪后,再开始下笔。
这和你平时操作图片、视频、网页容器的思路是一样的。先等资源或容器 ready,再做后续逻辑。
fillStyle 和 fillRect() 是最好的起手组合
案例只用了两个最基础的 API:
this.context.fillStyle = '#4D96FF'
this.context.fillRect(20, 20, 100, 60)
fillStyle 是设置填充颜色,fillRect(x, y, width, height) 是在指定位置画一个实心矩形。
拿这个组合作为入门,非常合适。因为你能立刻理解三件事:
- 颜色是状态
- 绘制命令会使用当前状态
- 位置和大小完全由坐标控制
后面你换成画圆、画文字、画路径,这套思维都是通用的。
坐标系一定要先建立直觉
案例还专门留了一块讲坐标系,这不是凑内容,是真的必要。
在 Canvas 里:
- 原点在左上角
x往右增大y往下增大
这跟很多数学坐标系不一样。你如果脑子里还是默认“y 轴向上”,一画就会错位。
举个例子:
this.context.fillRect(20, 20, 100, 60)
它的意思不是“从某个抽象位置开始画”,而是从左上角往右 20 像素、往下 20 像素的位置开始,画一个宽 100、高 60 的矩形。
这个案例为什么画了三个矩形
案例不是只画一个,而是连续画了三块:
this.context.fillRect(20, 20, 100, 60)
this.context.fillRect(140, 20, 100, 60)
this.context.fillRect(260, 20, 100, 60)
这其实是在让你感受“坐标布局”。同一行里,不同的 x 值决定了图形的横向排布。只要这个感觉建立起来,后面你做图表、图形阵列、卡片背景,思路会顺很多。
初学者最容易踩的坑
第一个坑,是没等 onReady() 就开始绘制。
第二个坑,是画布高度没给够,结果你以为没画出来,其实是被裁掉了。
第三个坑,是忘了 Canvas 和普通组件不一样,它画出来的内容不是声明式节点,而是一次次绘制结果。
第四个坑,是没有先建立坐标感,导致每次改位置都像碰运气。
学完这篇后,下一步该去哪
如果你已经能理解这页内容,接下来就应该顺着这个路线往下走:
- 学基本图形
- 学文字绘制
- 学渐变和样式
- 学动画刷新
- 学数据图表和交互
也就是说,先学“怎么画”,再学“画得更丰富”,最后学“画得会动、会表达数据”。
写在最后
Canvas 的第一步,其实比很多人想的简单。你只要记住三件事:创建上下文、绑定画布、在 onReady() 里下笔。剩下的内容,无非都是在这个框架上叠能力。
别嫌三个矩形简单。很多复杂图形的起点,真的就是这三个矩形。
更多推荐


所有评论(0)