9afce6f6创建于 2025年5月7日历史提交
/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { common2D, drawing } from '@kit.ArkGraphics2D';
import { DrawContext, FrameNode, NodeController, RenderNode, Size } from '@kit.ArkUI';
import { image } from '@kit.ImageKit';

/**
 * MyRenderNode类,初始化画笔和绘制路径
 */
export class MyRenderNode extends RenderNode {
  path: drawing.Path = new drawing.Path(); // 新建路径对象,用于绘制手指移动轨迹
  pen: drawing.Pen = new drawing.Pen(); // 创建一个画笔Pen对象,Pen对象用于形状的边框线绘制
  blendMode: drawing.BlendMode = drawing.BlendMode.SRC_OVER; // 画笔的颜色混合模式
  lineStrokeWidth: number = 0; // 画笔线宽

  constructor() {
    super();
    // 设置画笔颜色
    const pen_color: common2D.Color = {
      alpha: 0xFF,
      red: 0xFA,
      green: 0x64,
      blue: 0x00
    };
    this.pen.setColor(pen_color);
    // 设置画笔开启反走样,可以使得图形的边缘在显示时更平滑
    this.pen.setAntiAlias(true);
    // 开启画笔的抖动绘制效果。抖动绘制可以使得绘制出的颜色更加真实。
    this.pen.setDither(true);
    // 设置画笔绘制转角的样式为圆头
    this.pen.setJoinStyle(drawing.JoinStyle.ROUND_JOIN);
    // 设置画笔线帽的样式,即画笔在绘制线段时在线段头尾端点的样式为半圆弧
    this.pen.setCapStyle(drawing.CapStyle.ROUND_CAP);
  }

  // RenderNode进行绘制时会调用draw方法
  draw(context: DrawContext): void {
    const canvas = context.canvas;
    // 设置画笔的颜色混合模式,根据不同的混合模式实现涂鸦和擦除效果
    this.pen.setBlendMode(this.blendMode);
    // 设置画笔的线宽,单位px
    this.pen.setStrokeWidth(this.lineStrokeWidth);
    // 将Pen画笔设置到canvas中
    canvas.attachPen(this.pen);
    // 绘制path
    canvas.drawPath(this.path);
  }
}

/**
 * MyImageRenderNode类,绘制和记录画布图案的pixelMap
 */
export class MyImageRenderNode extends RenderNode {
  pixelMapHistory: image.PixelMap[] = []; // 记录每次绘制后画布的pixelMap
  cacheStack: image.PixelMap[] = []; // 记录撤销时从pixelMapHistory中出栈的pixelMap,恢复时使用

  // RenderNode进行绘制时会调用draw方法
  draw(context: DrawContext): void {
    const canvas = context.canvas;
    if (this.pixelMapHistory.length !== 0) {
      // 使用drawImage绘制pixelMapHistory栈顶的pixelMap
      canvas.drawImage(this.pixelMapHistory[this.pixelMapHistory.length - 1], 0, 0);
    }
  }
}

/**
 * NodeController的子类MyNodeController
 */
export class MyNodeController extends NodeController {
  private rootNode: FrameNode | null = null; // 根节点
  rootRenderNode: RenderNode | null = null; // 从NodeController根节点获取的RenderNode,用于添加和删除新创建的MyRenderNode实例

  // MyNodeController实例绑定的NodeContainer创建时触发,创建根节点rootNode并将其挂载至NodeContainer
  makeNode(uiContext: UIContext): FrameNode {
    this.rootNode = new FrameNode(uiContext);
    if (this.rootNode !== null) {
      this.rootRenderNode = this.rootNode.getRenderNode();
    }
    return this.rootNode;
  }

  // 绑定的NodeContainer布局时触发,获取NodeContainer的宽高
  aboutToResize(size: Size): void {
    if (this.rootRenderNode !== null) {
      // NodeContainer布局完成后设置rootRenderNode的背景透明
      this.rootRenderNode.backgroundColor = 0X00000000;
      // rootRenderNode的位置从组件NodeContainer的左上角(0,0)坐标开始,大小为NodeContainer的宽高
      this.rootRenderNode.frame = {
        x: 0,
        y: 0,
        width: size.width,
        height: size.height
      };
    }
  }

  // 添加节点
  addNode(node: RenderNode): void {
    if (this.rootNode === null) {
      return;
    }
    if (this.rootRenderNode !== null) {
      this.rootRenderNode.appendChild(node);
    }
  }

  // 清空节点
  clearNodes(): void {
    if (this.rootNode === null) {
      return;
    }
    if (this.rootRenderNode !== null) {
      this.rootRenderNode.clearChildren();
    }
  }
}