咱们开发者在开发 HarmonyOS 应用时,经常会遇到需要自定义绘制图形的需求,这时候 Canvas 组件就派上大用场啦。今天我就带着大家深入了解一下这个超实用的组件,从基础概念到代码实战,保证让你一看就懂、一学就会!

一、Canvas 组件到底是啥?能干啥?

简单来说,Canvas 组件就是 HarmonyOS 给咱们提供的一块 “画布”,咱们可以在上面自由绘制各种图形、文字,甚至进行 AI 图像分析。它从 API version 8 开始支持,后续版本还不断增加了新功能,像 API 12 + 就加入了 AI 分析相关的能力。

重点总结:

  1. 核心功能:提供自定义绘制图形的画布,支持 2D 绘图和 AI 图像分析(API 12+)。
  2. 设备支持:手机、PC/2in1、平板、穿戴设备都能用。
  3. 子组件:不支持嵌套子组件,只能单独使用。

代码示例(基础画布创建):

@Entry
@Component
struct CanvasBasicExample {
  build() {
    // 创建一个占满屏幕的Canvas
    Canvas()
      .width('100%')
      .height('100%')
      .backgroundColor('#f0f0f0') // 设置画布背景色
  }
}

这段代码就是创建了一个最简单的 Canvas,背景是浅灰色,占满整个屏幕。不过现在它还是空的,接下来咱们就来看看怎么在上面画画。

二、Canvas 接口详解:怎么初始化画布?

Canvas 组件有两种初始化方式,根据不同的 API 版本和需求来选择。

1. 基础接口(API 8+)

Canvas(context?: CanvasRenderingContext2D | DrawingRenderingContext)
  • 参数说明:context 可选,类型可以是 2D 渲染上下文或绘图上下文。
  • 注意事项:多个 Canvas 不能共用一个 context 对象,每个 Canvas 最好有自己的上下文。

2. 增强接口(API 12+)

Canvas(context: CanvasRenderingContext2D | DrawingRenderingContext, imageAIOptions: ImageAIOptions)

  • 新增参数:imageAIOptions,用于配置 AI 分析选项,比如设置分析类型或绑定控制器。
  • 适用场景:需要使用 AI 图像分析功能时使用这个接口。

重点总结:

  • 选择建议:单纯绘图用基础接口,需要 AI 分析用增强接口。
  • context 创建:通常在组件内部创建 context 对象,保证每个 Canvas 独立。

代码示例(初始化带 context 的 Canvas):

@Entry
@Component
struct CanvasWithContextExample {
  // 创建2D渲染上下文
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context2D: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  
  // 创建绘图上下文(API 12+)
  private contextDrawing: DrawingRenderingContext = new DrawingRenderingContext();
  
  build() {
    Flex({ direction: FlexDirection.Column }) {
      // 使用2D上下文的Canvas
      Text('2D渲染上下文画布')
        .fontSize(16)
        .margin(10)
      Canvas(this.context2D)
        .width('100%')
        .height(200)
        .backgroundColor('#e0e0e0')
      
      // 使用绘图上下文的Canvas(需要API 12+)
      Text('绘图上下文画布')
        .fontSize(16)
        .margin(10)
      Canvas(this.contextDrawing)
        .width('100%')
        .height(200)
        .backgroundColor('#e0e0e0')
    }
    .width('100%')
    .padding(10)
  }
}

这里创建了两个 Canvas,分别使用不同的上下文,注意绘图上下文(DrawingRenderingContext)是 API 12 + 才支持的哦。

三、Canvas 属性:怎么给画布加功能?

除了通用属性外,Canvas 还有一个特别的属性 ——enableAnalyzer,这是 API 12 + 新增的 AI 分析功能。

enableAnalyzer 属性

enableAnalyzer({ enable: boolean })

  • 作用:开启 Canvas 的 AI 分析功能,支持主体识别、文字识别、对象查找等。
  • 使用条件:需要搭配 CanvasRenderingContext2D 中的 startImageAnalyzer 和 stopImageAnalyzer 方法一起用。
  • 注意事项:不能和 overlay 属性同时使用,否则 overlay 会失效。

重点总结:

  • 开启 AI 分析:设置 enable 为 true,默认是 false。
  • 配套方法:必须调用 start 和 stop 方法来控制分析过程。
  • 设备依赖:该功能依赖设备能力,有些设备可能不支持。

代码示例(开启 AI 分析功能):

@Entry
@Component
struct CanvasAIAnalyzerExample {
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  private config: ImageAnalyzerConfig = {
    types: [ImageAnalyzerType.SUBJECT, ImageAnalyzerType.TEXT] // 分析主体和文字
  }
  private aiController: ImageAnalyzerController = new ImageAnalyzerController();
  
  build() {
    Column() {
      // 控制按钮
      Row() {
        Button('开始分析')
          .onClick(() => {
            this.context.startImageAnalyzer(this.config)
              .then(() => console.log('分析完成'))
              .catch((error) => console.log('分析错误:', error));
          })
        Button('停止分析')
          .onClick(() => this.context.stopImageAnalyzer())
      }
      .margin(10)
      
      // 开启AI分析的Canvas
      Canvas(this.context)
        .width(300)
        .height(200)
        .backgroundColor('#f5f5f5')
        .enableAnalyzer({ enable: true }) // 开启AI分析
        .onReady(() => {
          // 画布准备好后绘制内容
          this.context.fillStyle = '#007DFF';
          this.context.fillRect(50, 50, 200, 100);
        })
    }
    .width('100%')
    .padding(10)
  }
}

这个示例中,Canvas 开启了 AI 分析功能,点击按钮可以控制分析的开始和停止,配置了分析主体和文字两种类型。

四、Canvas 事件:怎么知道画布准备好了?

Canvas 有一个很重要的事件 ——onReady,用来监听画布初始化完成或大小变化。

onReady 事件

onReady(event: VoidCallback)

  • 触发时机:Canvas 初始化完成或大小变化时触发,触发后画布会被清空。
  • 使用场景:在这个事件回调里进行绘制操作,因为此时画布宽高确定,可以安全获取。
  • 注意事项:位置变化不会触发 onReady,只会触发 onAreaChange 事件,且 onAreaChange 在 onReady 之后触发。

重点总结:

  • 绘制时机:onReady 回调是进行绘制的最佳时机。
  • 事件顺序:先 onReady,再 onAreaChange(如果有大小变化)。
  • 避免重复绘制:注意处理多次触发的情况,比如大小变化时可能需要重新绘制。

代码示例(使用 onReady 事件绘制图形):

@Entry
@Component
struct CanvasOnReadyExample {
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  
  build() {
    // 自适应屏幕的Canvas
    Canvas(this.context)
      .width('90%')
      .height(300)
      .backgroundColor('#f9f9f9')
      .onReady(() => {
        console.log('画布准备好了,开始绘制...');
        // 绘制一个红色圆形
        this.context.fillStyle = 'red';
        this.context.beginPath();
        this.context.arc(150, 150, 100, 0, 2 * Math.PI);
        this.context.fill();
        
        // 绘制文字
        this.context.fillStyle = 'white';
        this.context.font = '20px Arial';
        this.context.textAlign = 'center';
        this.context.fillText('Hello Canvas!', 150, 150);
      })
  }
}

在 onReady 回调里,我们绘制了一个红色圆形和白色文字,这时候画布的宽高已经确定,不用担心绘制位置错误。

五、实战示例:三种常见用法场景

光看概念可能有点抽象,接下来咱们通过三个实战示例,看看 Canvas 在不同场景下怎么用。

示例 1:使用 CanvasRenderingContext2D 绘制基础图形

这个示例展示了最常用的 2D 绘图上下文的用法,适合绘制矩形、圆形、线条等基础图形。

@Entry
@Component
struct Canvas2DExample {
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  
  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
      Canvas(this.context)
        .width('90%')
        .height(400)
        .backgroundColor('#f0f8ff') // 浅蓝色背景
        .onReady(() => {
          // 绘制多个图形
          this.drawShapes();
        })
    }
    .width('100%')
    .padding(20)
  }
  
  // 绘制各种图形的方法
  drawShapes() {
    // 绘制矩形
    this.context.fillStyle = '#FFD700'; // 金色
    this.context.fillRect(50, 50, 100, 80); // 填充矩形
    
    // 绘制带边框的矩形
    this.context.strokeStyle = '#0000FF'; // 蓝色
    this.context.lineWidth = 3;
    this.context.strokeRect(200, 50, 100, 80); // 描边矩形
    
    // 绘制圆形
    this.context.fillStyle = '#FF69B4'; // 粉色
    this.context.beginPath();
    this.context.arc(125, 200, 50, 0, 2 * Math.PI);
    this.context.fill();
    
    // 绘制圆弧
    this.context.strokeStyle = '#228B22'; // 绿色
    this.context.lineWidth = 4;
    this.context.beginPath();
    this.context.arc(275, 200, 50, 0, Math.PI); // 半圆弧
    this.context.stroke();
    
    // 绘制线条
    this.context.strokeStyle = '#8B0000'; // 棕色
    this.context.lineWidth = 2;
    this.context.beginPath();
    this.context.moveTo(50, 300);
    this.context.lineTo(300, 300);
    this.context.stroke();
    
    // 绘制贝塞尔曲线
    this.context.strokeStyle = '#000000'; // 黑色
    this.context.lineWidth = 3;
    this.context.beginPath();
    this.context.moveTo(50, 350);
    this.context.quadraticCurveTo(175, 400, 300, 350);
    this.context.stroke();
  }
}

这个示例中,我们在 onReady 事件里调用了 drawShapes 方法,绘制了矩形、圆形、线条、贝塞尔曲线等多种图形,还设置了不同的颜色和线条宽度,是不是很直观?

示例 2:使用 DrawingRenderingContext 绘制图形(API 12+)

DrawingRenderingContext 是 API 12 + 新增的绘图上下文,用法和 CanvasRenderingContext2D 有点不同,咱们来看示例:

@Entry
@Component
struct CanvasDrawingExample {
  private context: DrawingRenderingContext = new DrawingRenderingContext();
  
  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
      Text('API 12+绘图上下文示例')
        .fontSize(18)
        .margin(10)
      
      Canvas(this.context)
        .width('90%')
        .height(300)
        .backgroundColor('#e6e6fa') // 淡紫色背景
        .onReady(() => {
          // 使用DrawingRenderingContext绘制
          this.drawWithDrawingContext();
        })
    }
    .width('100%')
    .padding(20)
  }
  
  drawWithDrawingContext() {
    // 获取画布对象
    const canvas = this.context.canvas;
    
    // 绘制蓝色圆形
    canvas.drawCircle(150, 150, 80, {
      fill: { color: '#1E90FF' }
    });
    
    // 绘制红色矩形
    canvas.drawRect({ x: 50, y: 50, width: 100, height: 80 }, {
      stroke: { color: '#FF0000', width: 3 }
    });
    
    // 绘制绿色文字
    canvas.drawText('DrawingContext示例', { x: 50, y: 250 }, {
      font: { size: 16, family: 'Arial' },
      fill: { color: '#008000' }
    });
    
    // 刷新画布
    this.context.invalidate();
  }
}

这里使用了 DrawingRenderingContext,它的绘制方式更面向对象一些,通过 canvas 对象的 drawCircle、drawRect 等方法来绘制,最后需要调用 invalidate () 来刷新画布。注意这个示例需要 API 12 + 哦。

示例 3:使用 attributeModifier 动态设置 Canvas 属性

有时候咱们需要动态修改 Canvas 的属性和方法,这时候 attributeModifier 就派上用场了,来看示例:

import { BusinessError } from '@ohos.basicServices'; // 假设引入方式正确

// 自定义属性修饰器
class MyCanvasModifier implements AttributeModifier<CanvasAttribute> {
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D();
  
  // 应用普通属性
  applyNormalAttribute(instance: CanvasAttribute): void {
    // 设置onReady事件,绘制图片
    instance.onReady(() => {
      // 这里假设image.png存在,实际使用时需要正确路径
      let image = new ImageBitmap('image.png');
      image.then((bitmap) => {
        this.context.drawImage(bitmap, 0, 0, 200, 200);
      });
    });
    
    // 开启AI分析功能
    instance.enableAnalyzer({ enable: true });
  }
  
  // 设置上下文
  setContext(context: CanvasRenderingContext2D) {
    this.context = context;
  }
}

@Entry
@Component
struct CanvasAttributeModifierExample {
  @State modifier: MyCanvasModifier = new MyCanvasModifier();
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  private config: ImageAnalyzerConfig = {
    types: [ImageAnalyzerType.SUBJECT, ImageAnalyzerType.TEXT]
  }
  private aiController: ImageAnalyzerController = new ImageAnalyzerController();
  private options: ImageAIOptions = {
    types: [ImageAnalyzerType.SUBJECT, ImageAnalyzerType.TEXT],
    aiController: this.aiController
  }
  
  build() {
    Column() {
      // 控制按钮
      Row() {
        Button('开始分析')
          .width(100)
          .onClick(() => {
            this.context.startImageAnalyzer(this.config)
              .then(() => console.log('分析完成'))
              .catch((error: BusinessError) => {
                console.log('错误码:', error.code);
              });
          })
        Button('停止分析')
          .width(100)
          .onClick(() => this.context.stopImageAnalyzer())
        Button('获取支持类型')
          .width(120)
          .onClick(() => {
            this.aiController.getImageAnalyzerSupportTypes()
              .then((types) => console.log('支持的分析类型:', types))
              .catch((error) => console.log('获取类型错误:', error));
          })
      }
      .margin(10)
      
      // 使用属性修饰器的Canvas
      Canvas(this.context, this.options)
        .width(250)
        .height(250)
        .borderWidth(1)
        .attributeModifier(this.modifier) // 应用属性修饰器
        .onAppear(() => {
          // 组件出现时设置上下文
          this.modifier.setContext(this.context);
        })
    }
    .width('100%')
    .padding(20)
  }
}

这个示例中,我们创建了一个自定义的属性修饰器 MyCanvasModifier,在里面动态设置 onReady 事件和 enableAnalyzer 属性,还可以通过按钮控制 AI 分析的过程,非常灵活。

六、使用 Canvas 的注意事项

最后咱们来总结一下使用 Canvas 时需要注意的点,避免踩坑:

  1. context 唯一性:多个 Canvas 不能共用一个 context 对象,每个 Canvas 都要有自己的上下文,否则会出问题。
  2. API 版本:注意不同功能的 API 版本要求,比如 DrawingRenderingContext 和 enableAnalyzer 都是 API 12 + 才支持的。
  3. 绘制时机:尽量在 onReady 事件里进行绘制,确保画布宽高已确定。
  4. AI 分析限制:enableAnalyzer 不能和 overlay 属性同时使用,否则 overlay 会失效。
  5. 设备能力:AI 分析功能依赖设备能力,有些设备可能不支持,需要做好兼容性处理。
  6. 性能优化:频繁绘制时注意性能,避免过多重绘影响应用流畅度。

结语

好了,关于 HarmonyOS 中的 Canvas 组件,咱们从概念到实战都过了一遍,相信大家已经对它有了比较清晰的认识。无论是绘制基础图形,还是使用 AI 分析功能,Canvas 都能满足咱们的需求。赶紧动手试试吧,在实际开发中多练习,很快就能熟练掌握啦!如果遇到问题,别忘了回头看看这些示例和注意事项哦~

Logo

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

更多推荐