想在 HarmonyOS 上画画先搞懂 Canvas、Brush 和 Pen你有没有想过那些绘图 APP 里画一条线、画一个圆、填个颜色底层是怎么实现的在 HarmonyOS 里2D 绘制靠的是ArkGraphics2D的 drawing 模块。这个模块提供了三个核心角色Canvas画布你画画的纸所有图形都画在它上面。Brush画刷控制填充——图形内部填什么颜色。Pen画笔控制描边——图形的轮廓线是什么样。打个比方Canvas 是一张白纸Brush 是你手里的马克笔用来涂色Pen 是你手里的勾线笔用来画轮廓。你想画一个红色填充、黑色边框的圆先用 Brush 设好红色再用 Pen 设好黑色然后告诉 Canvas “画个圆”就完事了。今天我们来做一个绘画 APP 的基础功能看看怎么用这三个类来画各种图形。下面是 Canvas 绘制的整体流程创建 Canvas 画布创建 Brush 画刷创建 Pen 画笔设置填充颜色/透明度设置描边颜色/线宽/线帽attachBrush 挂载画刷attachPen 挂载画笔调用绘制方法drawRect / drawCircle / drawLine 等detachBrush / detachPen 释放导入模块import{drawing}fromkit.ArkGraphics2D;所有 drawing 相关的类Canvas、Brush、Pen、Path 等都在这个模块里。创建 CanvasCanvas 需要一个绘制目标——它是一个 PixelMap。你可以把 Canvas 想象成一个画板PixelMap 就是画板上的纸。所有画上去的东西最终都会写入这个 PixelMap。import{drawing}fromkit.ArkGraphics2D;import{image}fromkit.ImageKit;constcolornewArrayBuffer(96);letopts:image.InitializationOptions{editable:true,pixelFormat:3,size:{height:4,width:6}}image.createPixelMap(color,opts).then((pixelMap){constcanvasnewdrawing.Canvas(pixelMap);})这里创建了一个 6x4 像素的 PixelMap然后用它创建了 Canvas。为什么 PixelMap 的editable要设为true因为 Canvas 需要往上面写入数据如果不可编辑就画不上去。用 RenderNode 来画在实际的 ArkUI 页面里你不太会直接new Canvas(pixelMap)来画。更常见的方式是通过RenderNode——它是 ArkUI 提供的一个自定义绘制节点可以在draw方法里拿到 Canvasimport{RenderNode}fromkit.ArkUI;import{common2D,drawing}fromkit.ArkGraphics2D;classDrawingRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvascontext.canvas;// 在这里画画...}}draw方法会在每次 UI 刷新时被调用context.canvas就是当前的画布。后面我们的示例都用这种方式。Brush控制填充Brush画刷控制的是图形内部的颜色。创建一个 Brush 很简单constbrushnewdrawing.Brush();设置颜色Brush 默认是黑色的。你可以用setColor改颜色// 方式一传 Color 对象constcolor:common2D.Color{alpha:255,red:255,green:0,blue:0};brush.setColor(color);// 方式二直接传四个值性能更好推荐brush.setColor(255,255,0,0);// alpha, red, green, blue颜色格式是 ARGB每个通道值范围[0, 255]。alpha是透明度255 是完全不透明0 是完全透明。方式二直接传四个数字性能更好因为它不需要创建 Color 对象。如果你在draw方法里频繁设置颜色优先用方式二。设置透明度brush.setAlpha(128);// 半透明把 Brush 挂到 Canvas 上设好颜色后需要把 Brush 挂到 Canvas 上Canvas 才知道用什么颜色来填充canvas.attachBrush(brush);// 画图操作...canvas.detachBrush();// 用完记得摘下来为什么要detach因为 Canvas 会一直记住当前挂的 Brush。如果你画完红色矩形后想画蓝色的不摘掉旧的 Brush新设的颜色就不会生效。用完就摘是个好习惯。Pen控制描边Pen画笔控制的是图形的轮廓线。用法和 Brush 几乎一样constpennewdrawing.Pen();pen.setColor(255,0,0,255);// 蓝色pen.setStrokeWidth(5);// 线宽 5 像素canvas.attachPen(pen);// 画图操作...canvas.detachPen();Pen 的独有属性Pen 比 Brush 多了一些描边相关的设置线宽pen.setStrokeWidth(5);// 5 像素宽线帽线条端点的样式pen.setStrokeCap(drawing.CapType.ROUND_CAP);// 圆头pen.setStrokeCap(drawing.CapType.SQUARE_CAP);// 方头pen.setStrokeCap(drawing.CapType.FLAT_CAP);// 平头默认连接样式两条线相交时的样式pen.setStrokeJoin(drawing.JoinStyle.MITER_JOIN);// 尖角pen.setStrokeJoin(drawing.JoinStyle.ROUND_JOIN);// 圆角pen.setStrokeJoin(drawing.JoinStyle.BEVEL_JOIN);// 平角虚线通过 PathEffect 实现后面文章会讲。画矩形绘制时需要根据需求选择挂载 Brush、Pen 或两者都挂下面是选择逻辑是否是否需要绘制图形只需要填充颜色?只挂 Brush只需要描边轮廓?只挂 Pen同时挂 Brush 和 Pen调用绘制方法画完后 detach 释放好Brush 和 Pen 都准备好了开始画图。最简单的就是矩形classDrawingRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvascontext.canvas;constpennewdrawing.Pen();pen.setStrokeWidth(5);pen.setColor({alpha:255,red:255,green:0,blue:0});canvas.attachPen(pen);canvas.drawRect({left:0,right:100,top:0,bottom:80});canvas.detachPen();}}drawRect接收一个Rect对象包含left、right、top、bottom四个值定义了矩形的位置和大小。从 API 12 开始还有一种性能更好的写法直接传四个数字canvas.drawRect(0,0,100,80);// left, top, right, bottom如果你只想填充不想描边就只挂 Brush只想描边不想填充就只挂 Pen两个都要就两个都挂// 红色填充 蓝色描边constbrushnewdrawing.Brush();brush.setColor(255,255,0,0);canvas.attachBrush(brush);constpennewdrawing.Pen();pen.setColor(255,0,0,255);pen.setStrokeWidth(3);canvas.attachPen(pen);canvas.drawRect(10,10,200,150);canvas.detachBrush();canvas.detachPen();画圆角矩形constroundRectnewdrawing.RoundRect({left:10,top:10,right:200,bottom:150},20,20// x 方向和 y 方向的圆角半径);canvas.drawRoundRect(roundRect);圆角半径越大角越圆。如果你想做那种胶囊形的按钮把圆角半径设成高度的一半就行。画圆canvas.drawCircle(100,100,50);// 圆心(100,100)半径50三个参数圆心 x 坐标、圆心 y 坐标、半径。如果半径小于等于 0什么都不画。画椭圆canvas.drawOval({left:10,top:10,right:200,bottom:100});椭圆的形状由它的外切矩形决定——矩形多扁椭圆就多扁。画弧线canvas.drawArc({left:10,top:10,right:200,bottom:200},0,90);三个参数外切矩形、起始角度度数、扫描角度度数。起始角度 0 是 3 点钟方向顺时针为正。上面这段画的是一个从 0 度到 90 度的四分之一圆弧。如果扫描角度的绝对值大于 360就变成画整个椭圆了。画直线canvas.drawLine(10,10,200,200);// 从(10,10)到(200,200)注意画直线只有描边效果没有填充效果。所以你需要挂 PenBrush 对直线没用。清空画布如果你想把画布擦干净用clear方法canvas.clear({alpha:255,red:255,green:255,blue:255});// 用白色清空这会用你指定的颜色填充整个画布的裁剪区域。效果等同于drawColor。完整示例画一个简单的图形页面来一个完整的例子在页面上画一个红色矩形、一个蓝色圆、一条绿色线import{RenderNode}fromkit.ArkUI;import{common2D,drawing}fromkit.ArkGraphics2D;classDrawingRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvascontext.canvas;// 画一个红色填充的矩形constbrushnewdrawing.Brush();brush.setColor(255,255,0,0);// 红色canvas.attachBrush(brush);canvas.drawRect(20,20,200,120);canvas.detachBrush();// 画一个蓝色边框的圆constpennewdrawing.Pen();pen.setColor(255,0,0,255);// 蓝色pen.setStrokeWidth(3);canvas.attachPen(pen);canvas.drawCircle(300,80,50);canvas.detachPen();// 画一条绿色的线constlinePennewdrawing.Pen();linePen.setColor(255,0,200,0);// 绿色linePen.setStrokeWidth(5);canvas.attachPen(linePen);canvas.drawLine(20,160,400,160);canvas.detachPen();}}每画一种图形都重新创建 Brush/Pen、设置颜色、attach、画、detach。这样最清晰不容易出错。你可能会问为什么要不停地 attach/detach不能设一次一直用吗可以但如果你后面要画不同颜色的图形就得重新设置。attach/detach 的方式更灵活——你可以随时切换不同的 Brush/Pen 组合。几个注意事项1. 单线程模型drawing 模块是单线程的Canvas 不是线程安全的。如果你在多个线程里同时操作同一个 Canvas会出问题。所有绘制操作都应该在同一个线程里完成。2. 单位是物理像素drawing 模块用的是屏幕物理像素px不是 dp。如果你的设备是 2x 密度屏100px 实际上是 50dp。在不同密度的设备上图形的大小可能会不一样。你可以用px2vp之类的工具函数做转换。3. 画布自带默认画刷Canvas 创建后自带一个默认画刷黑色、开启反走样。如果你没有 attach 任何 Brush/Pen画出来的图形就是黑色填充的。4. 顺序很重要先画的在下面后画的在上面。如果你先画了一个大矩形再画一个小圆小圆会盖在大矩形上面。小结Canvas Brush Pen 是 HarmonyOS 2D 绘制的基础三件套Canvas画布承载所有绘制操作。Brush画刷控制填充颜色和透明度。Pen画笔控制描边颜色、线宽、线帽、连接样式。用法就是创建 → 设置样式 → attach 到 Canvas → 画图 → detach。下一篇我们深入讲 Brush 和 Pen 的更多玩法比如渐变、阴影、自定义颜色矩阵。
鸿蒙开发-想在画布上画画?Canvas、Brush和Pen先学会
想在 HarmonyOS 上画画先搞懂 Canvas、Brush 和 Pen你有没有想过那些绘图 APP 里画一条线、画一个圆、填个颜色底层是怎么实现的在 HarmonyOS 里2D 绘制靠的是ArkGraphics2D的 drawing 模块。这个模块提供了三个核心角色Canvas画布你画画的纸所有图形都画在它上面。Brush画刷控制填充——图形内部填什么颜色。Pen画笔控制描边——图形的轮廓线是什么样。打个比方Canvas 是一张白纸Brush 是你手里的马克笔用来涂色Pen 是你手里的勾线笔用来画轮廓。你想画一个红色填充、黑色边框的圆先用 Brush 设好红色再用 Pen 设好黑色然后告诉 Canvas “画个圆”就完事了。今天我们来做一个绘画 APP 的基础功能看看怎么用这三个类来画各种图形。下面是 Canvas 绘制的整体流程创建 Canvas 画布创建 Brush 画刷创建 Pen 画笔设置填充颜色/透明度设置描边颜色/线宽/线帽attachBrush 挂载画刷attachPen 挂载画笔调用绘制方法drawRect / drawCircle / drawLine 等detachBrush / detachPen 释放导入模块import{drawing}fromkit.ArkGraphics2D;所有 drawing 相关的类Canvas、Brush、Pen、Path 等都在这个模块里。创建 CanvasCanvas 需要一个绘制目标——它是一个 PixelMap。你可以把 Canvas 想象成一个画板PixelMap 就是画板上的纸。所有画上去的东西最终都会写入这个 PixelMap。import{drawing}fromkit.ArkGraphics2D;import{image}fromkit.ImageKit;constcolornewArrayBuffer(96);letopts:image.InitializationOptions{editable:true,pixelFormat:3,size:{height:4,width:6}}image.createPixelMap(color,opts).then((pixelMap){constcanvasnewdrawing.Canvas(pixelMap);})这里创建了一个 6x4 像素的 PixelMap然后用它创建了 Canvas。为什么 PixelMap 的editable要设为true因为 Canvas 需要往上面写入数据如果不可编辑就画不上去。用 RenderNode 来画在实际的 ArkUI 页面里你不太会直接new Canvas(pixelMap)来画。更常见的方式是通过RenderNode——它是 ArkUI 提供的一个自定义绘制节点可以在draw方法里拿到 Canvasimport{RenderNode}fromkit.ArkUI;import{common2D,drawing}fromkit.ArkGraphics2D;classDrawingRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvascontext.canvas;// 在这里画画...}}draw方法会在每次 UI 刷新时被调用context.canvas就是当前的画布。后面我们的示例都用这种方式。Brush控制填充Brush画刷控制的是图形内部的颜色。创建一个 Brush 很简单constbrushnewdrawing.Brush();设置颜色Brush 默认是黑色的。你可以用setColor改颜色// 方式一传 Color 对象constcolor:common2D.Color{alpha:255,red:255,green:0,blue:0};brush.setColor(color);// 方式二直接传四个值性能更好推荐brush.setColor(255,255,0,0);// alpha, red, green, blue颜色格式是 ARGB每个通道值范围[0, 255]。alpha是透明度255 是完全不透明0 是完全透明。方式二直接传四个数字性能更好因为它不需要创建 Color 对象。如果你在draw方法里频繁设置颜色优先用方式二。设置透明度brush.setAlpha(128);// 半透明把 Brush 挂到 Canvas 上设好颜色后需要把 Brush 挂到 Canvas 上Canvas 才知道用什么颜色来填充canvas.attachBrush(brush);// 画图操作...canvas.detachBrush();// 用完记得摘下来为什么要detach因为 Canvas 会一直记住当前挂的 Brush。如果你画完红色矩形后想画蓝色的不摘掉旧的 Brush新设的颜色就不会生效。用完就摘是个好习惯。Pen控制描边Pen画笔控制的是图形的轮廓线。用法和 Brush 几乎一样constpennewdrawing.Pen();pen.setColor(255,0,0,255);// 蓝色pen.setStrokeWidth(5);// 线宽 5 像素canvas.attachPen(pen);// 画图操作...canvas.detachPen();Pen 的独有属性Pen 比 Brush 多了一些描边相关的设置线宽pen.setStrokeWidth(5);// 5 像素宽线帽线条端点的样式pen.setStrokeCap(drawing.CapType.ROUND_CAP);// 圆头pen.setStrokeCap(drawing.CapType.SQUARE_CAP);// 方头pen.setStrokeCap(drawing.CapType.FLAT_CAP);// 平头默认连接样式两条线相交时的样式pen.setStrokeJoin(drawing.JoinStyle.MITER_JOIN);// 尖角pen.setStrokeJoin(drawing.JoinStyle.ROUND_JOIN);// 圆角pen.setStrokeJoin(drawing.JoinStyle.BEVEL_JOIN);// 平角虚线通过 PathEffect 实现后面文章会讲。画矩形绘制时需要根据需求选择挂载 Brush、Pen 或两者都挂下面是选择逻辑是否是否需要绘制图形只需要填充颜色?只挂 Brush只需要描边轮廓?只挂 Pen同时挂 Brush 和 Pen调用绘制方法画完后 detach 释放好Brush 和 Pen 都准备好了开始画图。最简单的就是矩形classDrawingRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvascontext.canvas;constpennewdrawing.Pen();pen.setStrokeWidth(5);pen.setColor({alpha:255,red:255,green:0,blue:0});canvas.attachPen(pen);canvas.drawRect({left:0,right:100,top:0,bottom:80});canvas.detachPen();}}drawRect接收一个Rect对象包含left、right、top、bottom四个值定义了矩形的位置和大小。从 API 12 开始还有一种性能更好的写法直接传四个数字canvas.drawRect(0,0,100,80);// left, top, right, bottom如果你只想填充不想描边就只挂 Brush只想描边不想填充就只挂 Pen两个都要就两个都挂// 红色填充 蓝色描边constbrushnewdrawing.Brush();brush.setColor(255,255,0,0);canvas.attachBrush(brush);constpennewdrawing.Pen();pen.setColor(255,0,0,255);pen.setStrokeWidth(3);canvas.attachPen(pen);canvas.drawRect(10,10,200,150);canvas.detachBrush();canvas.detachPen();画圆角矩形constroundRectnewdrawing.RoundRect({left:10,top:10,right:200,bottom:150},20,20// x 方向和 y 方向的圆角半径);canvas.drawRoundRect(roundRect);圆角半径越大角越圆。如果你想做那种胶囊形的按钮把圆角半径设成高度的一半就行。画圆canvas.drawCircle(100,100,50);// 圆心(100,100)半径50三个参数圆心 x 坐标、圆心 y 坐标、半径。如果半径小于等于 0什么都不画。画椭圆canvas.drawOval({left:10,top:10,right:200,bottom:100});椭圆的形状由它的外切矩形决定——矩形多扁椭圆就多扁。画弧线canvas.drawArc({left:10,top:10,right:200,bottom:200},0,90);三个参数外切矩形、起始角度度数、扫描角度度数。起始角度 0 是 3 点钟方向顺时针为正。上面这段画的是一个从 0 度到 90 度的四分之一圆弧。如果扫描角度的绝对值大于 360就变成画整个椭圆了。画直线canvas.drawLine(10,10,200,200);// 从(10,10)到(200,200)注意画直线只有描边效果没有填充效果。所以你需要挂 PenBrush 对直线没用。清空画布如果你想把画布擦干净用clear方法canvas.clear({alpha:255,red:255,green:255,blue:255});// 用白色清空这会用你指定的颜色填充整个画布的裁剪区域。效果等同于drawColor。完整示例画一个简单的图形页面来一个完整的例子在页面上画一个红色矩形、一个蓝色圆、一条绿色线import{RenderNode}fromkit.ArkUI;import{common2D,drawing}fromkit.ArkGraphics2D;classDrawingRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvascontext.canvas;// 画一个红色填充的矩形constbrushnewdrawing.Brush();brush.setColor(255,255,0,0);// 红色canvas.attachBrush(brush);canvas.drawRect(20,20,200,120);canvas.detachBrush();// 画一个蓝色边框的圆constpennewdrawing.Pen();pen.setColor(255,0,0,255);// 蓝色pen.setStrokeWidth(3);canvas.attachPen(pen);canvas.drawCircle(300,80,50);canvas.detachPen();// 画一条绿色的线constlinePennewdrawing.Pen();linePen.setColor(255,0,200,0);// 绿色linePen.setStrokeWidth(5);canvas.attachPen(linePen);canvas.drawLine(20,160,400,160);canvas.detachPen();}}每画一种图形都重新创建 Brush/Pen、设置颜色、attach、画、detach。这样最清晰不容易出错。你可能会问为什么要不停地 attach/detach不能设一次一直用吗可以但如果你后面要画不同颜色的图形就得重新设置。attach/detach 的方式更灵活——你可以随时切换不同的 Brush/Pen 组合。几个注意事项1. 单线程模型drawing 模块是单线程的Canvas 不是线程安全的。如果你在多个线程里同时操作同一个 Canvas会出问题。所有绘制操作都应该在同一个线程里完成。2. 单位是物理像素drawing 模块用的是屏幕物理像素px不是 dp。如果你的设备是 2x 密度屏100px 实际上是 50dp。在不同密度的设备上图形的大小可能会不一样。你可以用px2vp之类的工具函数做转换。3. 画布自带默认画刷Canvas 创建后自带一个默认画刷黑色、开启反走样。如果你没有 attach 任何 Brush/Pen画出来的图形就是黑色填充的。4. 顺序很重要先画的在下面后画的在上面。如果你先画了一个大矩形再画一个小圆小圆会盖在大矩形上面。小结Canvas Brush Pen 是 HarmonyOS 2D 绘制的基础三件套Canvas画布承载所有绘制操作。Brush画刷控制填充颜色和透明度。Pen画笔控制描边颜色、线宽、线帽、连接样式。用法就是创建 → 设置样式 → attach 到 Canvas → 画图 → detach。下一篇我们深入讲 Brush 和 Pen 的更多玩法比如渐变、阴影、自定义颜色矩阵。