1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { common2D, drawing } from '@kit.ArkGraphics2D'; 17import { DrawContext, FrameNode, NodeController, RenderNode, Size } from '@kit.ArkUI'; 18import { image } from '@kit.ImageKit'; 19 20/** 21 * MyRenderNode类,初始化画笔和绘制路径 22 */ 23export class MyRenderNode extends RenderNode { 24 path: drawing.Path = new drawing.Path(); // 新建路径对象,用于绘制手指移动轨迹 25 pen: drawing.Pen = new drawing.Pen(); // 创建一个画笔Pen对象,Pen对象用于形状的边框线绘制 26 blendMode: drawing.BlendMode = drawing.BlendMode.SRC_OVER; // 画笔的颜色混合模式 27 lineStrokeWidth: number = 0; // 画笔线宽 28 29 constructor() { 30 super(); 31 // 设置画笔颜色 32 const penColor: common2D.Color = { 33 alpha: 0xFF, 34 red: 0xFA, 35 green: 0x64, 36 blue: 0x00 37 }; 38 this.pen.setColor(penColor); 39 // 设置画笔开启反走样,可以使得图形的边缘在显示时更平滑 40 this.pen.setAntiAlias(true); 41 // 开启画笔的抖动绘制效果。抖动绘制可以使得绘制出的颜色更加真实。 42 this.pen.setDither(true); 43 // 设置画笔绘制转角的样式为圆头 44 this.pen.setJoinStyle(drawing.JoinStyle.ROUND_JOIN); 45 // 设置画笔线帽的样式,即画笔在绘制线段时在线段头尾端点的样式为半圆弧 46 this.pen.setCapStyle(drawing.CapStyle.ROUND_CAP); 47 } 48 49 // RenderNode进行绘制时会调用draw方法 50 draw(context: DrawContext): void { 51 const canvas = context.canvas; 52 // 设置画笔的颜色混合模式,根据不同的混合模式实现涂鸦和擦除效果 53 this.pen.setBlendMode(this.blendMode); 54 // 设置画笔的线宽,单位px 55 this.pen.setStrokeWidth(this.lineStrokeWidth); 56 // 将Pen画笔设置到canvas中 57 canvas.attachPen(this.pen); 58 // 绘制path 59 canvas.drawPath(this.path); 60 } 61} 62 63/** 64 * MyImageRenderNode类,绘制和记录画布图案的pixelMap 65 */ 66export class MyImageRenderNode extends RenderNode { 67 pixelMapHistory: image.PixelMap[] = []; // 记录每次绘制后画布的pixelMap 68 cacheStack: image.PixelMap[] = []; // 记录撤销时从pixelMapHistory中出栈的pixelMap,恢复时使用 69 70 // RenderNode进行绘制时会调用draw方法 71 draw(context: DrawContext): void { 72 const canvas = context.canvas; 73 if (this.pixelMapHistory.length !== 0) { 74 // 使用drawImage绘制pixelMapHistory栈顶的pixelMap 75 canvas.drawImage(this.pixelMapHistory[this.pixelMapHistory.length - 1], 0, 0); 76 } 77 } 78} 79 80/** 81 * NodeController的子类MyNodeController 82 */ 83export class MyNodeController extends NodeController { 84 private rootNode: FrameNode | null = null; // 根节点 85 rootRenderNode: RenderNode | null = null; // 从NodeController根节点获取的RenderNode,用于添加和删除新创建的MyRenderNode实例 86 87 // MyNodeController实例绑定的NodeContainer创建时触发,创建根节点rootNode并将其挂载至NodeContainer 88 makeNode(uiContext: UIContext): FrameNode { 89 this.rootNode = new FrameNode(uiContext); 90 if (this.rootNode !== null) { 91 this.rootRenderNode = this.rootNode.getRenderNode(); 92 } 93 return this.rootNode; 94 } 95 96 // 绑定的NodeContainer布局时触发,获取NodeContainer的宽高 97 aboutToResize(size: Size): void { 98 if (this.rootRenderNode !== null) { 99 // NodeContainer布局完成后设置rootRenderNode的背景透明 100 this.rootRenderNode.backgroundColor = 0X00000000; 101 // rootRenderNode的位置从组件NodeContainer的左上角(0,0)坐标开始,大小为NodeContainer的宽高 102 this.rootRenderNode.frame = { 103 x: 0, 104 y: 0, 105 width: size.width, 106 height: size.height 107 }; 108 } 109 } 110 111 // 添加节点 112 addNode(node: RenderNode): void { 113 if (this.rootNode === null) { 114 return; 115 } 116 if (this.rootRenderNode !== null) { 117 this.rootRenderNode.appendChild(node); 118 } 119 } 120 121 // 清空节点 122 clearNodes(): void { 123 if (this.rootNode === null) { 124 return; 125 } 126 if (this.rootRenderNode !== null) { 127 this.rootRenderNode.clearChildren(); 128 } 129 } 130}