为一篇讲 HarmonyOS7 Canvas 基础绘制的技术教程绘制一张手绘框架图,主题是 Canv

为 HarmonyOS7 Canvas 入门实战绘制一张手绘流程图,表现“为什么绘制逻辑要放在 on

为一篇技术教程文章绘制一张手绘课堂笔记风格的信息图,主题是 HarmonyOS7 Canvas 从零

前言

很多人第一次接触 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,再做后续逻辑。

fillStylefillRect() 是最好的起手组合

案例只用了两个最基础的 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() 里下笔。剩下的内容,无非都是在这个框架上叠能力。

别嫌三个矩形简单。很多复杂图形的起点,真的就是这三个矩形。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐