做过Android开发的同学对View的自定义绘制应该多少都有点了解,那么再接触鸿蒙的自定义绘制就好上手了。

工具介绍

在Android中常用的工具,和画笔

画布:Canvas 

画笔:Paint

在鸿蒙中其实也是这样:

画布:Canvas

画笔:CanvasRenderingContext2D 和 OffscreenCanvasRenderingContext2D

那么这两个画笔有什么区别呢?

  • CanvasRenderingContext2D 使用CanvasRenderingContext2D对象在Canvas画布上绘制。
  • OffscreenCanvasRenderingContext2D :离屏绘制,是指将需要绘制的内容先绘制在缓存区,再将其转换成图片,一次性绘制到Canvas上,加快了绘制速度。过程为:

    • 通过transferToImageBitmap方法将离屏画布最近渲染的图像创建为一个ImageBitmap对象。
    • 通过CanvasRenderingContext2D对象的transferFromImageBitmap方法显示给定的ImageBitmap对象。
常用的方法
//常用方法:
fill() //(对封闭路径进行填充)
stroke() //(进行边框绘制操作)
clip() //(设置当前路径为剪切路径)
fillStyle() //(指定绘制的填充色)
globalAlpha() //(设置透明度)
strokeStyle() //(设置描边的颜色)等属性修改绘制内容的样式
fillText() //(文本填充
strokeText() //(文本描边)
arc() //(绘制弧线路径
ellipse() //(绘制一个椭圆)
rect() //(创建矩形路径)
drawImage() //(图像绘制)
putImageData() //(使用ImageData数据填充新的矩形区域)
createImageData() //(创建新的ImageData 对象
getPixelMap() //(以当前canvas指定区域内的像素创建PixelMap对象
getImageData() //(以当前canvas指定区域内的像素创建ImageData对象
createLinearGradient() //(创建一个线性渐变色)
createRadialGradient() //(创建一个径向渐变色)

代码示例

接下来用一个小例子帮大家熟悉一下鸿蒙的自定义绘制

@ComponentV2
export struct DemoPage {
  // 配置渲染上下文设置,开启抗锯齿
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  // 创建 2D 渲染上下文
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  // 创建路径对象,用于复杂图形绘制
  private path: Path2D = new Path2D();
  // 动态文本内容
  @Local text: string = 'ArkTS Canvas';

  aboutToAppear(): void {
    // 预先定义一个路径,将在 onReady 中使用
    this.path.arc(150, 500, 50, 0, Math.PI * 2);
  }

  build() {
    Column() {
      // 标题
      Text("Canvas API 演示")
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 20 })

      // Canvas 绘制区域
      Canvas(this.context)
        .width('100%')
        .height('80%')
        .backgroundColor('#f5f5f5')
        .onReady(() => {
          this.drawScene();
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }

  // 绘制整个场景
  private drawScene(): void {
    // 清空画布
    this.context.clearRect(0, 0, 1000, 1000);

    // 1. 绘制背景 - 使用径向渐变
    this.drawBackground();

    // 2. 绘制太阳 - 使用圆形和径向渐变
    this.drawSun();

    // 3. 绘制房子 - 使用矩形和路径
    this.drawHouse();

    // 4. 绘制树 - 使用路径和填充
    this.drawTree();

    // 5. 绘制文本 - 使用填充文本和描边文本
    this.drawTexts();

    // 6. 使用预定义路径绘制一个圆
    this.context.fillStyle = '#3366CC';
    this.context.fill(this.path);
    this.context.strokeStyle = '#000000';
    this.context.lineWidth = 2;
    this.context.stroke(this.path);
  }

  // 绘制渐变背景
  private drawBackground(): void {
    // 创建线性渐变 - 从顶部到底部
    let skyGradient = this.context.createLinearGradient(0, 0, 0, 600);
    skyGradient.addColorStop(0.0, '#87CEEB'); // 天空蓝
    skyGradient.addColorStop(0.7, '#E0F7FA'); // 浅蓝
    skyGradient.addColorStop(1.0, '#81C784'); // 草地绿

    // 填充背景
    this.context.fillStyle = skyGradient;
    this.context.fillRect(0, 0, 1000, 600);
  }

  // 绘制太阳
  private drawSun(): void {
    // 创建径向渐变
    let sunGradient = this.context.createRadialGradient(100, 100, 10, 100, 100, 50);
    sunGradient.addColorStop(0.0, '#FFEB3B'); // 亮黄色
    sunGradient.addColorStop(1.0, '#FF9800'); // 橙色

    // 绘制太阳圆形
    this.context.beginPath();
    this.context.arc(100, 100, 50, 0, Math.PI * 2);
    this.context.fillStyle = sunGradient;
    this.context.fill();

    // 绘制太阳光芒
    this.context.strokeStyle = '#FFEB3B';
    this.context.lineWidth = 3;
    for (let i = 0; i < 12; i++) {
      const angle = (Math.PI / 6) * i;
      this.context.beginPath();
      this.context.moveTo(100 + Math.cos(angle) * 50, 100 + Math.sin(angle) * 50);
      this.context.lineTo(100 + Math.cos(angle) * 70, 100 + Math.sin(angle) * 70);
      this.context.stroke();
    }
  }

  // 绘制房子
  private drawHouse(): void {
    // 房子主体 - 矩形
    this.context.fillStyle = '#795548';
    this.context.fillRect(300, 300, 200, 150);

    // 房顶 - 路径
    this.context.beginPath();
    this.context.moveTo(300, 300);
    this.context.lineTo(400, 220);
    this.context.lineTo(500, 300);
    this.context.closePath();
    this.context.fillStyle = '#D32F2F';
    this.context.fill();

    // 门 - 矩形
    this.context.fillStyle = '#5D4037';
    this.context.fillRect(375, 375, 50, 75);

    // 窗户 - 矩形和清除区域
    this.context.fillStyle = '#90CAF9';
    this.context.fillRect(325, 325, 50, 50);
    this.context.fillRect(425, 325, 50, 50);

    // 窗户框架
    this.context.strokeStyle = '#000000';
    this.context.lineWidth = 2;
    this.context.strokeRect(325, 325, 50, 50);
    this.context.strokeRect(425, 325, 50, 50);

    // 窗户十字架
    this.context.beginPath();
    this.context.moveTo(350, 325);
    this.context.lineTo(350, 375);
    this.context.moveTo(325, 350);
    this.context.lineTo(375, 350);
    this.context.moveTo(450, 325);
    this.context.lineTo(450, 375);
    this.context.moveTo(425, 350);
    this.context.lineTo(475, 350);
    this.context.stroke();
  }

  // 绘制树
  private drawTree(): void {
    // 树干
    this.context.fillStyle = '#8D6E63';
    this.context.fillRect(600, 350, 20, 100);

    // 树叶 - 使用椭圆
    this.context.fillStyle = '#388E3C';

    // 底部椭圆
    this.context.beginPath();
    this.context.ellipse(610, 350, 60, 40, 0, 0, Math.PI * 2);
    this.context.fill();

    // 中间椭圆
    this.context.beginPath();
    this.context.ellipse(610, 320, 50, 35, 0, 0, Math.PI * 2);
    this.context.fill();

    // 顶部椭圆
    this.context.beginPath();
    this.context.ellipse(610, 290, 40, 30, 0, 0, Math.PI * 2);
    this.context.fill();
  }

  // 绘制文本
  private drawTexts(): void {
    // 标题文本 - 填充
    this.context.font = '40px bolder sans-serif';
    this.context.fillStyle = '#000000';
    this.context.fillText('美丽的风景', 50, 50);

    // 动态文本 - 描边
    this.context.font = '30px sans-serif';
    this.context.strokeStyle = '#FF5722';
    this.context.lineWidth = 1;
    this.context.strokeText(this.text, 50, 580);

    // 组合文本 - 填充和描边
    this.context.font = '24px sans-serif';
    this.context.fillStyle = '#FFFFFF';
    this.context.fillText('Canvas 绘图示例', 300, 450);
    this.context.strokeStyle = '#000000';
    this.context.lineWidth = 0.5;
    this.context.strokeText('Canvas 绘图示例', 300, 450);
  }
}

Logo

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

更多推荐