# 如何让Canvas绘制内容能超出父组件的限制

#### 问题现象

如何让Canvas绘制内容能超出父组件的限制，“按下”的响应区域不超出父组件范围？组件中.clip属性能实现此效果吗？

#### 背景知识

*   HarmonyOS组件中.clip属性：是否对子组件超出当前组件范围外的区域进行裁剪。参考链接：clip。
*   HarmonyOS组件中.responseRegion属性可以实现组件的响应区域范围的变化，响应区域范围可以超出或者小于组件的布局范围。参考链接：自定义控制的多层级手势事件。
*   Canvas：Canvas组件提供画布，用于自定义绘制图形。
*   CanvasRenderingContext2D：使用CanvasRenderingContext2D在Canvas画布组件上进行绘制，绘制对象可以是矩形、文本、图片等。

#### 解决方案

在HarmonyOS中，无法通过.clip属性实现所需效果。当.clip属性设置为true时，子组件超出当前组件范围的区域将不会响应绑定的手势事件；当设置为undefined时，系统将不再对超出部分进行裁剪，但这并不意味着可以控制内容超出组件本身的绘制范围。可以通过以下方案实现让Canvas绘制内容能超出父组件限制的功能，步骤如下：

1.  使用Column父组件包含子组件Canvas，Column父组件宽高为(150,150)区域，子组件Canvas宽高为(300,300)。
2.  对Canvas组件设置.responseRegion属性触摸热区为.responseRegion({ x: 75, y: 0, width: 150, height: 150 })。实现功能：蓝色区域（150\*150）可响应Canvas绘制内容时“按下（down）”的触摸操作，红色区域（300\*300去除蓝色区域的范围）不可响应“按下（down）”的触摸操作，红色区域只可响应从蓝色区域“按下（down）”后的“移动（move）”触摸操作。

当组件Canvas绑定了.responseRegion(Rect)，所有落在Rect区域范围的触摸事件和手势可被组件Canvas对应的回调响应。

```
@Entry
@Component
struct DrawBankCom {
  paintSize: number = 5; // 当前画笔大小
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  tempPath: Path2D = new Path2D();
  @State @Watch('onChange') pathArray: Array<Path2D> = []; // 所有画图路径信息
  isUpdate: boolean = true;
  removeArray: Array<Path2D> = []; // 回退的路径集合
  @State text: string = '';
  @State eventType: string = '';

  build() {
    Column() {
      if (this.isUpdate) {
        Row() {
          Column() {
            Canvas(this.canvasContext)
              // Canvas(子组件)宽高设置为300*300
              .width(300)
              .height(300)
              .onReady(() => {
                this.canvasContext.lineWidth = this.paintSize;
                this.canvasContext.stroke(this.tempPath);
                for (let index = 0; index < this.pathArray.length; index++) {
                  this.canvasContext.stroke(this.pathArray[index]);
                }
              })
              .onTouch((event?: TouchEvent) => {
                if (event) {
                  if (event.type === TouchType.Down) {
                    this.eventType = 'Down';
                    this.canvasContext.lineWidth = this.paintSize;
                    this.canvasContext.beginPath();
                    this.tempPath = new Path2D();
                    this.pathArray.push(this.tempPath);
                    this.tempPath.moveTo(event.touches[0].x, event.touches[0].y);
                    this.canvasContext.moveTo(event.touches[0].x, event.touches[0].y);
                  }
                  if (event.type === TouchType.Up) {
                    this.eventType = 'Up';
                  }
                  if (event.type === TouchType.Move) {
                    this.eventType = 'Move';
                    this.tempPath.lineTo(event.touches[0].x, event.touches[0].y);
                    this.canvasContext.stroke(this.tempPath);
                    this.canvasContext.lineTo(event.touches[0].x, event.touches[0].y);
                    this.canvasContext.stroke();
                  }
                  this.text = 'TouchType:' + this.eventType + '\n touch point and touch element:\nx: ' +
                  event.touches[0].x + '\n' + 'y: ' + event.touches[0].y + '\nwidth:' +
                  event.target.area.width + '\nheight:' + event.target.area.height + '\pathArray size:' +
                  this.pathArray.length;
                }
              })
              .renderFit(RenderFit.TOP_RIGHT)
                // 设置触摸热区，蓝色区域可响应“down”操作，红色不可响应“down”操作，只能响应“move”操作
              .responseRegion({
                x: 75,
                y: 0,
                width: 150,
                height: 150
              });
          }
          // Column(父组件)宽高设置为150*150
          .width(150)
          .height(150)
          .backgroundColor('#ffbdf3e9');
        };
      }

      Column() {
        Row() {
          Button('回退').onClick(() => {
            this.pathArray.pop();
          });
        };

        Text(this.text);
      };
    }
    .width(300)
    .height(300)
    .clip(false)
    .borderRadius(10)
    .backgroundColor('#ff0000');
  }

  onChange() {
    this.canvasContext.reset();
    this.canvasContext.lineWidth = this.paintSize;
    for (let index = 0; index < this.pathArray.length; index++) {
      this.canvasContext.stroke(this.pathArray[index]);
    }
  }
}
```

具体表现为从蓝色区域开始画线，可以延伸到红色区域，但是不可从红色区域开始画线。

#### 总结

使用.responseRegion属性可以实现组件的响应区域范围的变化，能实现让Canvas绘制内容能超出父组件的限制。