【HarmonyOS】Canvas签字板实践

403 阅读5分钟

Canvas提供画布组件,用于自定义绘制图形,开发者使用CanvasRenderingContext2D对象在Canvas组件上进行绘制,绘制对象可以是基础形状、文本、图片等。

1. 基本使用:

Canvas(context: CanvasRenderingContext2D | DrawingRenderingContext)
参数名类型必填说明
contextCanvasRenderingContext2D |DrawingRenderingContext绘画上下文

注: API12以上,可添加imageAIOptions参数,给组件设置AI分析选项,通过此项可配置分析类型或绑定一个分析控制器。

使用步骤:

  1. 放置Canvas组件-设置宽和高
  2. 初始化画笔对象 CanvasRenderingContext2D,将画笔对象作为构造参数传递给Canvas组件
  3. 可以在Canvas的onReady事件中进行动态绘制

简单示例:

@Entry
@Component
struct CanvasDemo {
  /* 准备绘制上下文 */  
  ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D()

  build() {
    Canvas(this.ctx)
      .width('100%')
      .aspectRatio(1)
      .backgroundColor('#f1f1f1')
      .onReady(() => {
         //绘制矩形
         this.ctx.beginPath();
         this.ctx.rect(100, 50, 100, 100);
         this.ctx.stroke();
         //绘制圆形
         this.ctx.beginPath();
         this.ctx.arc(150, 250, 50, 0, 6.28);
         this.ctx.stroke();
         //绘制椭圆
         this.ctx.beginPath();
         this.ctx.ellipse(150, 450, 50, 100, Math.PI * 0.25, Math.PI * 0, Math.PI * 2);
         this.ctx.stroke();
      })
  }
}

上述代码中用到了onReady事件,是Canvas组件初始化完成时或者Canvas组件发生大小变化时的事件回调,当该事件被触发时画布被清空。当Canvas组件仅发生位置变化时,只触发onAreaChange事件、不触发onReady事件。onAreaChange事件在onReady事件后触发。

2. 绘制上下文

可以用来绘制文本、图形,处理像素等,是Canvas组件的核心。常用接口有fill(对封闭路径进行填充)、clip(设置当前路径为剪切路径)、stroke(进行边框绘制操作)等等,同时提供了fillStyle(指定绘制的填充色)、globalAlpha(设置透明度)与strokeStyle(设置描边的颜色)等属性修改绘制内容的样式。将通过以下几个方面简单介绍画布组件常见使用方法:

基础形状绘制。 可以通过arc(绘制弧线路径)、 ellipse(绘制一个椭圆)、rect(创建矩形路径)等接口绘制基础形状。

2.1. 常用属性
属性类型说明
fillStylestring |number |CanvasGradient指定绘制的填充色(可设置渐变色)
lineWidthnumber设置绘制线条的宽度,不支持0和负数
strokeStylestring |number |CanvasGradient设置线条的颜色(可设置渐变色)
fontstring设置文本绘制中的字体样式。
textAlignCanvasTextAlign设置文本绘制中的文本对齐方式
globalAlphanumber设置透明度
heightnumber组件高度
widthnumber组件宽度
directionCanvasDirection用于设置绘制文字时使用的文字方向。

说明: fillStylestrokeStyle的参数类型:

  • 类型为string时,表示设置线条使用的颜色,默认值:'black'
  • 类型为number时,表示设置线条使用的颜色。默认值:'#000000'
  • 类型为CanvasGradient时,表示渐变对象,使用createLinearGradient方法创建
/* 渐变色示例 */
let grad = this.context.createLinearGradient(50,0, 300,100)	// 线性渐变色
let grad = this.context.createRadialGradient(200,200,50, 200,200,200)	// 径向渐变色
grad.addColorStop(0.0, '#ff0000')
grad.addColorStop(0.5, '#ffffff')
grad.addColorStop(1.0, '#00ff00')

font语法格式:ctx.font='font-style font-weight font-size font-family'

2.2. 常用方法:
  1. fillRect(x, y, width, height):绘制一个填充矩形。
  2. strokeRect(x, y, width, height):绘制一个只有边框的矩形。
  3. clearRect(x, y, width, height):清除指定矩形区域内的内容。
  4. fillText(text, x, y, maxWidth):在画布上绘制填充文本。
  5. strokeText(text, x, y, maxWidth):在画布上绘制描边文本。
  6. beginPath():开始一条新的路径。
  7. closePath():关闭当前路径。
  8. moveTo(x, y):将路径移动到画布上的指定点。
  9. lineTo(x, y):在当前位置和指定点之间创建直线。
  10. arc(x, y, radius, startAngle, endAngle, anticlockwise):绘制一个弧形路径。
  11. ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise):绘制椭圆
  12. rect(x, y, width, height):创建一个矩形路径。
  13. fill(path):对指定路径进行填充。
  14. stroke(path):对指定路径进行描边。
  15. drawImage(image, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight):在画布上绘制图像。
  16. toDataURL(type?, quality?) 生成一个包含图片展示的URL
    1. type:可选参数,用于指定图像格式,默认格式为image/png
    2. quality: 图片质量可以从0到1的区间内选择图片的质量,默认值0.92
  1. reset():重置画板为默认状态
  2. restore():对保存的绘图上下文进行恢复

其他详情方法请参考:CanvasRenderingContext2D方法

3. 配合Touch事件签字

关键代码:

  .onTouch((event: TouchEvent) => {
    let x = event.touches[0].x
    let y = event.touches[0].y
    // 手指按下,开始绘画
    if (event.type === TouchType.Down) {
      this.ctx.beginPath()
      this.ctx.moveTo(x, y)
    // 手指移动,画笔移动
    } else if (event.type === TouchType.Move) {
      this.ctx.lineTo(x, y)
      this.ctx.stroke()
    // 手指抬起,结束绘画
    } else if (event.type === TouchType.Up) {
      this.ctx.closePath()
    }

其他功能:

  1. 保存图片: toDataURL(),返回包含图片展示的URL
Button('生成图片')
  .onClick(() => {
    this.canvasImage = this.ctx.toDataURL("image/png", 0.92)
  })
  1. 清空画板: reset()
Button('清空画板')
  .onClick(() => {
      this.ctx.reset()
  })

完整代码:

@Entry
@Component
struct CanvasDemo {
  ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D()
  @State canvasImage: string = ''

  build() {
    Column({ space: 20 }) {
      Canvas(this.ctx)
        .width('100%')
        .aspectRatio(1)
        .backgroundColor('#f1f1f1')
        .onReady(() => {
          // 设置画笔的颜色和宽度
          this.ctx.strokeStyle = '#000'
          this.ctx.lineWidth = 2
        })
        .onTouch((event: TouchEvent) => {
          let x = event.touches[0].x
          let y = event.touches[0].y
          // 手指按下,开始绘画
          if (event.type === TouchType.Down) {
            this.ctx.beginPath()
            this.ctx.moveTo(x, y)
          } else if (event.type === TouchType.Move) {
            this.ctx.lineTo(x, y)
            this.ctx.stroke()
          } else if (event.type === TouchType.Up) {
            this.ctx.closePath()
          }
        })

      Row({ space: 20 }) {
        Button('清空画板')
          .onClick(() => {
              this.ctx.reset()
          })
        Button('生成图片')
          .onClick(() => {
            this.canvasImage = this.ctx.toDataURL("image/png", 0.92)
          })
      }
      if (this.canvasImage) {
        Image(this.canvasImage)
          .width(200)
          .aspectRatio(1)
          .backgroundColor('#fff')
      }
    }
    .padding(10)
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Pink)
  }
}