Canvas实现镂空效果
============

#### 问题现象

如何使用Canvas组件实现镂空效果？

#### 背景知识

*   [Canvas](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-components-canvas-canvas)：提供画布的组件，用于自定义绘制图形，其基于[CanvasRenderingContext2D](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-canvasrenderingcontext2d)进行绘制，绘制对象可以是矩形、文本、图片等。
*   [clearRect](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-offscreencanvasrenderingcontext2d#clearrect)：删除指定区域内的绘制内容。
*   [blendMode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-image-effect#blendmode11)：将当前控件的内容（包含子节点内容）与下方画布（可能为离屏画布）已有内容进行混合。

#### 解决方案

*   **方案一**：在Canvas组件中，通过CanvasRenderingContext2D的[rect](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-canvasrenderingcontext2d#rect)方法绘制一个矩形，并将[globalCompositeOperation](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-canvasrenderingcontext2d#globalcompositeoperation)属性设置为xor，以实现图层的异或合成效果。由于xor模式会使重叠区域的颜色值为透明，因此可以通过该方式使绘制区域与背景产生透明重叠，从而达到镂空的视觉效果。
    
    import { display } from '@kit.ArkUI';
    
    @Entry
    @Component
    struct ClearRectOne {
      private settings: RenderingContextSettings \= new RenderingContextSettings(true);
      private context: CanvasRenderingContext2D \= new CanvasRenderingContext2D(this.settings);
    
      build() {
        Stack({ alignContent: Alignment.TopStart }) {
          Canvas(this.context)
            .width('100%')
            .height('100%')
            .onReady(() \=\> {
            _  // 设置全局合成操作模式为XOR_
              this.context.globalCompositeOperation \= 'xor';
              this.context.beginPath();
           _   // 计算屏幕宽度和高度并绘制矩形_
              this.context.rect(0, 0, this.getUIContext().px2vp(display.getDefaultDisplaySync().width),
                this.getUIContext().px2vp(display.getDefaultDisplaySync().height) \- 40);
              this.context.closePath();
              this.context.fillStyle \= '#000000';
              this.context.fill();
              this.context.beginPath();
              this.context.rect(100, 100, 200, 200);
              this.context.closePath();
              this.context.fillStyle \= '#000000';
              this.context.fill();
            })
            .opacity(0.6);
        }
        .backgroundColor(Color.Yellow)
        .width('100%')
        .height('100%');
      }
    }
    
*   **方案二**：使用clearRect清除部分区域，形成镂空效果。
    
    @Entry
    @Component
    struct ClearRectTwo {
      private settings: RenderingContextSettings \= new RenderingContextSettings(true);
      private context: CanvasRenderingContext2D \= new CanvasRenderingContext2D(this.settings);
    
      build() {
        Stack({ alignContent: Alignment.TopStart }) {
          Canvas(this.context)
            .width(200)
            .height(200)
            .backgroundColor(undefined)
            .onReady(async () \=\> {
            _  // 设置填充色_
              this.context.fillStyle \= '#000000';
           _   // 绘制覆盖整个Canvas的矩形_
              this.context.fillRect(0, 0, 140, 140);
           _   // 在矩形中心位置创建镂空区域_
              this.context.clearRect(40, 40, 60, 60);
            })
            .opacity(0.6);
        }
        .backgroundColor(Color.Yellow)
        .padding({ top: 100, left: 120 })
        .width('100%')
        .height('100%');
      }
    }
    
*   **方案三**：除了通过Canvas方法实现外，还可以通过设置重叠组件的blendMode属性，实现重叠部分透明的效果。
    
    @Entry
    @Component
    struct ClearRectThree {
      build() {
        Stack() {
         _ // 背景设置_
          Column() {
          }
          .width('100%')
          .height('100%')
          .backgroundColor(Color.Yellow);
    
          Stack() {
          _  // 绘制镂空区域_
            Circle({ width: 50, height: 50 })
              .blendMode(BlendMode.XOR, BlendApplyType.OFFSCREEN);
          }
          .blendMode(BlendMode.SRC\_OVER, BlendApplyType.OFFSCREEN)
          .backgroundColor('rgba(0, 0, 0, 1)')
          .width(80)
          .height(80);
        }
        .width('100%')
        .height('100%');
      }
    }