当前位置: 首页 > news >正文

鸿蒙开发-想让绘制更好看?渐变、阴影和混合模式

上一篇我们学了 Canvas + Brush + Pen 的基础用法——画矩形、画圆、填颜色、描边。但如果你真的在做一个绘画 APP,光有这些是不够的。你还需要渐变色、半透明、抗锯齿、阴影……

今天我们来深入看看 Brush 和 Pen 的进阶功能。

下面是 Brush 和 Pen 进阶功能的整体结构:

Brush / Pen 进阶功能

抗锯齿 setAntiAlias

透明度 setAlpha

渐变色 setShaderEffect

阴影 setShadowLayer

混合模式 setBlendMode

颜色滤波 setColorFilter

图像滤波 setImageFilter

线性渐变

径向渐变

扫描渐变

抗锯齿:让边缘更平滑

你有没有注意过,画一个圆的时候,边缘可能会有锯齿感——就像一排阶梯一样。这是因为屏幕是由像素点组成的,斜线和曲线在像素网格上不可能完美平滑。

抗锯齿(Anti-Aliasing)的原理很简单:在边缘处用半透明的像素做过渡,让阶梯感变弱。Brush 和 Pen 都支持开启抗锯齿:

constbrush=newdrawing.Brush();brush.setAntiAlias(true);// 开启抗锯齿constpen=newdrawing.Pen();pen.setAntiAlias(true);// 开启抗锯齿

开启后,图形的边缘会更平滑,但可能会稍微模糊一点。一般情况下建议开启,除非你在做像素画那种需要锐利边缘的风格。

透明度:做出层次感

brush.setAlpha(128);// 半透明pen.setAlpha(64);// 更透明

取值范围[0, 255],0 完全透明(看不见),255 完全不透明。

透明度能做什么?很多效果都离不开它:

  • 半透明的色块叠加,做出"磨砂玻璃"效果
  • 多层半透明图形叠加,做出深度感
  • 淡入淡出动画(通过动态改变 alpha 值)

渐变色:不只是纯色

纯色太平淡了?用ShaderEffect可以做出渐变效果。ShaderEffect 支持三种渐变:

线性渐变

从一个点到另一个点,颜色平滑过渡:

constshaderEffect=drawing.ShaderEffect.createLinearGradient({x:0,y:0},// 起点{x:300,y:0},// 终点[0xFFFF0000,0xFF0000FF],// 红色到蓝色drawing.TileMode.CLAMP// 平铺模式);brush.setShaderEffect(shaderEffect);canvas.attachBrush(brush);canvas.drawRect(0,0,300,200);canvas.detachBrush();

这段代码画了一个从左到右、红到蓝渐变的矩形。

colors数组里放的是 32 位 ARGB 颜色值。格式是0xAARRGGBB,比如0xFFFF0000是不透明的红色,0xFF0000FF是不透明的蓝色。

TileMode控制渐变超出范围后怎么处理:

  • CLAMP:用边缘颜色填充剩余区域
  • REPEAT:重复渐变
  • MIRROR:镜像重复
  • DECAL:只在原始范围内渲染

径向渐变

从圆心向外扩散的渐变:

constshaderEffect=drawing.ShaderEffect.createRadialGradient({x:150,y:100},// 圆心100,// 半径[0xFFFF0000,0xFF00FF00],// 红色到绿色drawing.TileMode.CLAMP);brush.setShaderEffect(shaderEffect);

这种渐变就像阳光从中心向外扩散,中心是红色,边缘是绿色。适合做按钮的"发光"效果或者背景的"聚光灯"效果。

扫描渐变

像雷达扫描一样,绕着圆心旋转的渐变:

constshaderEffect=drawing.ShaderEffect.createSweepGradient({x:150,y:100},// 圆心[0xFFFF0000,0xFF0000FF,0xFF00FF00],// 红→蓝→绿drawing.TileMode.CLAMP,0,// 起始角度360// 结束角度);brush.setShaderEffect(shaderEffect);

适合做圆形进度条、色轮选择器之类的 UI。

面对三种渐变类型,如何选择?可以参考下面的流程:

单方向过渡

从中心向外扩散

绕中心旋转

需要渐变效果

渐变方向是什么?

线性渐变 createLinearGradient

径向渐变 createRadialGradient

扫描渐变 createSweepGradient

适合: 背景渐变、按钮

适合: 发光效果、聚光灯

适合: 圆形进度条、色轮

渐变也能用在 Pen 上

不只是 Brush,Pen 也支持 ShaderEffect:

constpen=newdrawing.Pen();pen.setStrokeWidth(10);pen.setShaderEffect(shaderEffect);canvas.attachPen(pen);canvas.drawLine(0,0,300,200);// 渐变色的线条canvas.detachPen();

一条从红到蓝渐变的线,是不是很酷?

阴影:给文字加立体感

ShadowLayer可以给绘制内容加阴影。不过目前只在绘制文字时生效,图形暂时不支持。

import{common2D,drawing}from'@kit.ArkGraphics2D';// 创建阴影层:模糊半径、x偏移、y偏移、颜色letcolor:common2D.Color={alpha:0xFF,red:0x00,green:0x00,blue:0x00};letshadowLayer=drawing.ShadowLayer.create(3,-3,3,color);constbrush=newdrawing.Brush();brush.setShadowLayer(shadowLayer);canvas.attachBrush(brush);// 在这里绘制文字...canvas.detachBrush();

create的四个参数:

  • blurRadius:阴影的模糊半径,值越大阴影越"散"
  • x:阴影在 x 方向的偏移,正值向右,负值向左
  • y:阴影在 y 方向的偏移,正值向下,负值向上
  • color:阴影的颜色

比如你想做一个"文字投影"效果:create(5, 2, 2, 黑色)就是向右下方偏移 2 像素、模糊半径 5 的黑色阴影。

混合模式:颜色叠加的魔法

当两个图形重叠时,重叠区域的颜色怎么算?这就是混合模式(BlendMode)决定的。

brush.setBlendMode(drawing.BlendMode.SRC_OVER);// 默认模式

SRC_OVER是默认模式——后画的盖在先画的上面。其他常见的混合模式还有:

  • SRC:只显示后画的,忽略先画的
  • DST_OVER:后画的在先画的下面
  • SRC_IN:只显示后画的和先画的重叠部分(用后画的颜色)
  • DST_IN:只显示重叠部分(用先画的颜色)
  • MULTIPLY:正片叠底,颜色相乘,重叠区域变暗
  • SCREEN:滤色,重叠区域变亮

混合模式在做"图层特效"时很有用。比如你想做一个"叠加"效果,就用MULTIPLY;想做"发光"效果,就用SCREEN

颜色滤波器:调整颜色

ColorFilter可以对颜色做变换,比如转灰度、调色相等:

letcolorFilter=drawing.ColorFilter.createSRGBGammaToLinear();brush.setColorFilter(colorFilter);

ColorFilter提供了多种创建方式:

  • createSRGBGammaToLinear():sRGB gamma 转线性
  • createLinearToSRGBGamma():线性转 sRGB gamma
  • createFromMatrix(colorMatrix):自定义颜色矩阵(和 effectKit 的 setColorMatrix 类似)

颜色滤波器会作用在所有绘制的颜色上,相当于给整个画面加了一层"颜色滤镜"。

图像滤波器:模糊和锐化

ImageFilter可以对绘制结果做图像处理,比如模糊:

letimageFilter=drawing.ImageFilter.createBlur(5,5,drawing.TileMode.CLAMP,null);brush.setImageFilter(imageFilter);

effectKit的 blur 不同,这里的模糊是实时的——每次绘制都会应用。适合做"毛玻璃"效果的 UI 元素。

Pen 也支持 ImageFilter:

letcolorFilter=drawing.ColorFilter.createSRGBGammaToLinear();letimgFilter=drawing.ImageFilter.createFromColorFilter(colorFilter);pen.setImageFilter(imgFilter);

完整示例:渐变色卡片

来一个实际的例子——画一个带渐变背景和圆角的卡片:

import{RenderNode}from'@kit.ArkUI';import{common2D,drawing}from'@kit.ArkGraphics2D';classCardRenderNodeextendsRenderNode{draw(context:DrawContext){constcanvas=context.canvas;// 渐变背景:从左上到右下的蓝紫渐变constbrush=newdrawing.Brush();constgradient=drawing.ShaderEffect.createLinearGradient({x:0,y:0},{x:300,y:200},[0xFF6200EE,0xFFBB86FC],drawing.TileMode.CLAMP);brush.setShaderEffect(gradient);brush.setAntiAlias(true);canvas.attachBrush(brush);// 画圆角矩形constroundRect=newdrawing.RoundRect({left:20,top:20,right:320,bottom:220},16,16);canvas.drawRoundRect(roundRect);canvas.detachBrush();}}

这段代码画了一个从深紫到浅紫渐变的圆角卡片。渐变 + 圆角 + 抗锯齿,三者组合起来就已经很好看了。

几个实用技巧

1. ShaderEffect 设为 null 可以清除

brush.setShaderEffect(null);// 清除渐变,回到纯色模式

Brush 和 Pen 的所有"设置"方法都支持传null来清除效果(setShaderEffect、setColorFilter、setImageFilter、setShadowLayer、setPathEffect)。

2. 拷贝构造可以复用样式

如果你要画很多相同样式的图形,可以用拷贝构造函数避免重复设置:

constbrush=newdrawing.Brush();brush.setColor(255,255,0,0);brush.setAntiAlias(true);// 拷贝一份,两个 Brush 样式一样constbrush2=newdrawing.Brush(brush);

Pen 也一样:new drawing.Pen(pen)

3. 渐变坐标是绝对像素

createLinearGradient的起点和终点用的是画布上的绝对坐标,不是相对坐标。如果你画了一个矩形从 (100, 100) 到 (400, 300),渐变的起点终点也要在这个范围内才有意义。

小结

Brush 和 Pen 的进阶功能:

功能BrushPen说明
抗锯齿setAntiAliassetAntiAlias让边缘更平滑
透明度setAlphasetAlpha0-255
渐变色setShaderEffectsetShaderEffect线性/径向/扫描
阴影setShadowLayersetShadowLayer仅文字生效
混合模式setBlendMode-颜色叠加方式
颜色滤波setColorFiltersetColorFilter颜色变换
图像滤波setImageFiltersetImageFilter模糊等效果
路径效果-setPathEffect虚线等

下一篇我们来看 Path(路径)——怎么画直线、曲线、贝塞尔曲线,做出各种复杂的形状。

http://www.cnnetsun.cn/news/2669849.html

相关文章:

  • HEIF Utility:Windows用户处理苹果HEIF图片的终极解决方案
  • 告别传统求解器:用PyTorch实现傅立叶神经算子(FNO),让PDE求解快1000倍
  • 别再让GC卡顿毁掉你的游戏!Unity垃圾回收优化实战(附Profiler排查技巧)
  • 从传感器融合到机器人定位:手把手拆解卡尔曼滤波中的‘信息加权平均’是怎么算出来的
  • 基于DOM解析与样式提取的HTML到Figma转换技术深度解析
  • 终极指南:免费解密网易云音乐NCM文件,ncmdumpGUI完整使用教程
  • 如何让智能电视变身全能上网终端:TV Bro电视浏览器实战指南
  • 告别抖动!用Unity Cinemachine 2D Camera实现丝滑角色跟随(附参数调优指南)
  • Win7离线环境救星:手把手教你修改XML和注册表,彻底解决VMware Converter 6.2无法启动服务
  • UE5独立游戏开发避坑:UI多语言切换为啥必须用独立进程测试?
  • 【rsyslog服务】把所有服务的“临界点”以上的错误都保存在/var/log/alert.log⽇志中
  • 手把手调试ZYNQ的AXI DMA:从Vivado连线到SDK代码的全流程问题定位指南
  • LabVIEW事件队列架构选型
  • 告别破解风险:手把手教你用官方试用版+合法授权方式体验SecureCRT核心功能
  • FPGA开发板吃灰?用拨码开关和LED灯做个四位乘法器实验(Quartus II + Cyclone IV保姆级教程)
  • 城市大脑架构解析:从云计算、大数据到AI的智慧城市中枢构建
  • 别再手动标ROI了!用C#和Halcon的HSmartWindowControl实现交互式绘制与参数一键导出
  • 别再折腾了!保姆级教程:从Qt5.9.8到5.12.3的平滑升级与VS2022环境配置(附常见报错全解)
  • 2026利雅得全球AI展:洞察趋势、链接生态、把握中东AI机遇
  • AI信息过载时代:如何构建高效个人知识管理系统与通讯订阅策略
  • 用户说“好用”,但留存暴跌?:用因果推断+会话片段锚定技术,精准定位反馈失真源头
  • 避坑指南:Linux安装openGauss时遇到的‘防火墙’和‘权限’那些事儿
  • 用PyTorch实现FNO(傅里叶神经算子):一个解决偏微分方程的AI新范式
  • 别再手动传Jar包了!Mycat2 1.21版本一键部署脚本(附避坑点)
  • AI项目落地难?四大认知偏差与决策陷阱的识别与应对
  • 解决Chrome浏览器无法下载Keil MDK安装文件的问题
  • AI与IoT如何重塑智能汽车驾驶体验:从技术原理到三层进化
  • ChatGPT辅助Python爬虫开发:从静态抓取到反爬策略实战
  • VASP计算完别急着关!手把手教你从OUTCAR、CONTCAR里‘挖’出有用数据(附常用grep命令)
  • 别被NAND骗了!CM211-1 MC022盒子刷Armbian保姆级教程(S905L3+EMMC实战)