鸿蒙纪·梦始卷#11 | 画板绘制 - 认识绘制

311 阅读6分钟

鸿蒙纪11.png

《鸿蒙纪元》张风捷特烈 计划打造的一套 HarmonyOS 开发系列教程合集。致力于创作优质的鸿蒙原生学习资源,帮助开发者进入纯血鸿蒙的开发之中。本系列的所有代码将开源在 HarmonyUnit 项目中:

github: github.com/toly1994328…
gitee: gitee.com/toly1994328…

鸿蒙纪元 系列文章列表可在《文章总集》 或 【github 项目首页】 查看。

前面 10 篇,我们从环境搭建、到计数器猜数字电子木鱼,一路走来,应该对鸿蒙开发有了那么一丢丢的认知。接下来将进入另外一个篇章。在这里将充满创造力,来体验一下鸿蒙开发中 Canvas 自定义绘制的能力,实现一个简单的小画板。


1. 需求与交互介绍

白板绘制是本教程的第三个案例,相比于前两个项目,可操作性更强一些,也更有趣。适合新手朋友进一步了解 绘制相关知识,体会其创造性,绘制过程中练习语法也是个不错的选择。下面是两个最基础的交互:

  • 通过监听用户的拖拽手势,让界面留下触点的线条痕迹。
  • 可以选择绘制时线条的颜色和粗细两个配置项。
画板绘制颜色和线宽选择
134.gif135.gif

对于界面绘制内容的管理,提供了两个功能:

  • 当界面中存在绘制的线条时,可以回退上一步;在有回退历史时,可以撤销回退。
  • 右上角的清除按钮,点击时会弹出对话框确认清除,完成清空绘制的功能。
回退和撤销清除内容
136.gif137.gif

2. 从画点开始说起

鸿蒙 ArkUI 中的自定义绘制和 h5 基本上是一致的,所以有过绘制校验的小伙伴就不用太担心。首先搭建一个如下界面,画几个点在界面上:

回退和撤销清除内容
Screenshot_2024-11-15T093151.pngScreenshot_2024-11-15T093122.png

在 pages 中,创建 painter 文件夹,盛放画板绘制相关的代码。view 下的 Painter 文件将作为构建界面的场所:

image.png

构建代码如下,其中 AppBar 是我们之前封装的组件,前面也用过很多次了,这里就不赘述了。绘制主要使用 Canvas 组件,该组件在构造时需要传入 CanvasRenderingContext2D 对象;该对象就是绘制操作的核心:

import { AppBar } from "../../../components/AppBar"

@Component
export struct Painter {

  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  
  @Builder
  title() { //略... 详见源码 }

  @Builder
  backButton() { //略... 详见源码 }

  @Builder
  actionsButton() { //略... 详见源码 }

  build() {
    Column() {
      AppBar({
          titleSlot: this.title,
          tailing: () => {this.backButton()},
          leading: () => {this.actionsButton()},
        })
      Canvas(this.context)
        .width('100%')
        .layoutWeight(1)
        .backgroundColor('#fafafa')
        .onReady(() => this.paint(this.context))
    }
    .width('100%').height('100%')
  }

  paint(canvas: CanvasRenderingContext2D) {
    // TODO 绘制...
  }
}

鸿蒙的绘制 api 并没有 Flutter 那样丰富,绘制点可以 fillRect 绘制矩形完成:

image.png

paint(canvas: CanvasRenderingContext2D) {
  let strokeWidth = 10;
  canvas.fillRect(100, 100, strokeWidth, strokeWidth)
  canvas.fillRect(100, 150, strokeWidth, strokeWidth)
  canvas.fillRect(150, 150, strokeWidth, strokeWidth)
  canvas.fillRect(200, 100, strokeWidth, strokeWidth)
}

3. 边线粗细和颜色

鸿蒙绘制中 画板画笔路径 的操作全都集成到了 CanvasRenderingContext2D 身上。

边线粗细边线颜色
Screenshot_2024-11-15T123219.pngScreenshot_2024-11-15T123449.png
  • 通过 lineWidth 可以控制线的宽度;
  • 通过 strokeStyle 可以控制线的颜色;
  • 通过 moveTo 可以移动路径;
  • 通过 lineTo 可以在到某点形成直线路径;
  • 通过 stroke 绘制出边线路径;
  drawLine1(canvas: CanvasRenderingContext2D){
    canvas.lineWidth = 10;
    canvas.strokeStyle = '#000000';
    canvas.beginPath();
    canvas.moveTo(100, 100);
    canvas.lineTo(100, 150);
    canvas.lineTo(150, 150);
    canvas.lineTo(200,100);
    canvas.stroke();
  }

4. 线的端点类型

线的端点类型有三种,如下所示:

  • square: 方形头
  • butt : 无头
  • round: 圆头

image.png

canvas.lineCap = 'square';
canvas.lineCap = 'butt';
canvas.lineCap = 'round';

5. 简单的基础图形绘制

CanvasRenderingContext2D 中提供了一些基础图形的绘制,比如 圆形矩形圆角矩形椭圆圆弧 等,这里简单了解一下。Path2D 提供了路径的封装,可以构建基本图形路径:

arc 绘制弧形: 入参分别是圆心坐标、半径 、 起始弧度,终止弧度。

另外,fill 会填满内部;可以将stroke 是线型模式,如下右图:

image.png

paint(canvas: CanvasRenderingContext2D) {
  let region = new Path2D();
  region.arc(100, 100, 50, 0, 2*Math.PI);
  canvas.fill(region)
  canvas.translate(120,0)
  canvas.stroke(region)
}

Path2D#rect 可以构建矩形路径:

// 矩形
let region = new Path2D();
region.rect(100, 100, 80, 60);
canvas.fill(region)
canvas.translate(120,0)
canvas.stroke(region)

圆角矩形并没有内置提供,我们可以简单封装一下,通过直线和圆弧的路径操作来实现:

this.roundedRect(canvas,100, 100, 80, 60,8);
canvas.stroke()
canvas.translate(120, 0)
canvas.fill()

// 绘制圆角矩形的函数
roundedRect(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number) {
  ctx.beginPath();
  ctx.moveTo(x + radius, y); // 移动到左上角圆角的起始点
  ctx.lineTo(x + width - radius, y); // 绘制上边直线
  ctx.arcTo(x + width, y, x + width, y + height, radius); // 绘制右上角圆角
  ctx.lineTo(x + width, y + height - radius); // 绘制右边直线
  ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius); // 绘制右下角圆角
  ctx.lineTo(x + radius, y + height); // 绘制下边直线
  ctx.arcTo(x, y + height, x, y + height - radius, radius); // 绘制左下角圆角
  ctx.lineTo(x, y + radius); // 绘制左边直线
  ctx.arcTo(x, y, x + radius, y, radius); // 绘制左上角圆角
  ctx.closePath();
}

Path2D#ellipse 可以构建矩形路径:参数分别是: 原点坐标、长短轴长、旋转角度、起止弧度

image.png

let region = new Path2D();
region.ellipse(120, 450, 80, 120, 0, Math.PI * 0, Math.PI * 2)
canvas.fill(region)
canvas.translate(0, -260)
canvas.lineWidth = 5;
canvas.stroke(region)

这里提交一个小里程碑: v17-简单绘制


4. 本章小结

本章主要介绍了如何在鸿蒙中通过 CanvasRenderingContext2D 自定义绘制内容。界面就相当于一张白纸、绘制接口方法就相当于画笔,使用已经存在的组件固然简单,但学会自己控制画笔绘制内容可以创造更多的精彩。当然,想要精通绘制也不是一朝一夕可以达成的,但凡工艺技能,都是熟能生巧。

这里只是简单认识了绘制的方式,接下来将结合手势和绘制,完成在手指在界面上拖拽留下痕迹的绘制效果。

另外,现在的数据都是存储在内存中的,应用退出之后无论是选项,还是功德记录都会重置。想要数据持久化存储,在后面的 数据的持久化存储 章节会再继续完善,木鱼项目先告一段落。


更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。关注 公众号 并回复 鸿蒙纪元 可领取最新的 xmind 脑图电子版,让我们一起成长,变得更强。我们下次再见~