尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Flutter 绘制实践 | 路径篇 - 阴影模糊

Flutter 绘制实践 | 路径篇 - 阴影模糊 欢迎来到 Flutter 绘制实践系列本文有文章版和视频版。视频发布在 bilibli 同名账号下文章首发于掘金平台。今天来的话题是绘制阴影Flutter 绘制实践系列视频链接Flutter 绘制实践 | 第一集 · 画板尺寸Flutter 绘制实践 | 第二集 · 坐标系Flutter 绘制实践 | 第三集 · 画板更新Flutter 绘制实践 | 第四集 · 动画数值Flutter 绘制实践 | 第五集 · 坐标轴范围Flutter 绘制实践 | 第六集 · 函数曲线Flutter 绘制实践 | 路径篇 · 雪花1Flutter 绘制实践 | 路径篇 · 雪花2Flutter 绘制实践 | 路径篇 · 变换中心Flutter 绘制实践 | 路径篇 · 阴影模糊1.阴影的绘制说起 Flutter 绘制阴影很多朋友可能都知道Canvas本身有drawShadow方法。可以根据入参的 Path 路径绘制阴影。就像我们男人分不清口红色号一样觉得口红就是红色很多编程者也会觉得阴影就是影子。如果一个方法就能绘制出完美的阴影也不会单独写篇文章。Canvas 中提供的drawShadow方法是根据影深 elevation进行绘制阴影的。如下所示是drawShadow绘制圆角矩形路径的效果其中第四入是bool值用于控制路径内部是否填充transparentOccluder:truetransparentOccluder:falseoverride void paint(Canvas canvas, Size size) { canvas.translate(size.width / 2, size.height / 2); Path path Path() ..addRRect( RRect.fromRectAndRadius(Rect.fromPoints( Offset.zero, const Offset(100,60), ), const Radius.circular(8)), ); canvas.drawShadow(path, Colors.blue.withOpacity(0.2), 5, false); }一般来说不会只是单绘制一个路径阴影还要绘制影子的发出者。如下所示在阴影上方绘制白色的实物。这样阴影的效果就有了并且随着 elevation 的增加阴影会越深void drawBox(Canvas canvas,double elevation){ Path path Path() ..addRRect( RRect.fromRectAndRadius(Rect.fromPoints( Offset.zero, const Offset(100,60), ), const Radius.circular(8)), ); canvas.drawShadow(path, Colors.blue.withOpacity(0.2), elevation, false); Paint whitePaint Paint()..colorColors.white; canvas.drawPath(path,whitePaint); }其实可以发现drawShadow只能根据影深来控制阴影效果无法调节阴影偏移量、阴影半径、阴影扩散等属性。所以我们需要寻找到一种途径来绘制更加复杂的路径阴影。比如 css 中的阴影样式定义:2. BoxDecoration 中阴影的使用在日常开发中我们或多或少使用过装饰属性比如Container和DecoratedBox组件可以通过BoxDecoration确定装饰的效果。其中就包含对阴影的处理而且和 css 中的阴影样式是一致的css 中 box-shadow 是一个列表每组由五个参数分别表示x偏移量 | y偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色而 Flutter 中的BoxDecoration#boxShadow也是一个列表其中配置参数由BoxShadow类记录。参数的作用和 css 中的是一致的offset 表示偏移量blurRadius 表示阴影模糊半径spreadRadius 表示阴影扩散半径。这样如果已知 css 中的阴影样式就很容易将其在 Flutter 中展示出来。如下是Element UI的全局弹框的阴影效果在 Flutter 中的表现源码详见 box_decoration// ElementUI 阴影 BoxDecoration element BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), border: Border.all(color:const Color(0xffebeef5)), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), offset: const Offset(0,2), blurRadius: 12, spreadRadius: 0, ) ], );如下是Ant Design全局弹框的阴影效果其中的 box-shadow 由三个阴影确定对于 Flutter 而言就是在boxShadow中提供多个BoxShadow对象// Ant Design 阴影 BoxDecoration decoration BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), offset: const Offset(0,6), blurRadius: 16, spreadRadius: 0, ), BoxShadow( color: Colors.black.withOpacity(0.12), offset: const Offset(0,3), blurRadius: 6, spreadRadius: -4, ), BoxShadow( color: Colors.black.withOpacity(0.05), offset: const Offset(0,9), blurRadius: 28, spreadRadius: 8, ) ], );3. BoxDecoration 是如何绘制的既然 BoxDecoration 可以灵活地处理阴影样式那么问题来了在 Canvas 的绘制中如何使用呢其实仔细思考一下Flutter 中的一切组件都是绘制出来的那么BoxDecoration自然也不例外。那么只要查阅源码看一下它的绘制逻辑自然可以知道这种阴影是如何实现的。那接下来开始探秘吧。首先来看DecoratedBox组件对应的渲染对象的绘制方法。如下所示BoxDecoration 装饰对象通过createBoxPainter方法创建绘制对象也就是说 BoxDecoration 只承载绘制的信息绘制的逻辑由绘制对象承担。 通过源码可以看出创建的绘制对象是_BoxDecorationPainter, 也就是说该类中可能会是绘制阴影逻辑发生的场所果然不出所料绘制阴影的核心逻辑就在这个类中会遍历boxShadow列表绘制阴影区域。其中偏移量 offset 是矩形区域的移动扩散半径 spreadRadius 是矩形区域的扩大最后剩下一个模糊长度 blurRadius 还未知在上面 447 行中画笔是通过boxShadow对象会取的进入其中可以发现画笔会设置一个模糊的蒙版。蒙版的模糊参数由blurRadius确定。到这里我们就清楚了BoxDecoration 实现阴影的本质是通过模糊蒙版。所以我们也可以借鉴这种思路来处理绘制时的路径阴影。double get blurSigma convertRadiusToSigma(blurRadius);4. 通过 BoxShadow 绘制阴影BoxDecoration 本身用于矩形类的阴影绘制像路径这种不规则的图形不能直接使用。所以需要进行一些处理特别是spreadRadius对阴影的扩散处理。如下是封装的方法路径的偏移很简单可以通过shift进行处理。难点在于spreadRadius的处理这里对path沿矩形区域中心进行缩放来实现扩散的效果。关于 Path 的简单变换在上一篇已经介绍过了。void drawShadows(Canvas canvas, Path path, ListBoxShadow shadows) { for (final BoxShadow shadow in shadows) { final Paint shadowPainter shadow.toPaint(); if (shadow.spreadRadius 0) { canvas.drawPath(path.shift(shadow.offset), shadowPainter); } else { Rect zone path.getBounds(); double xScale (zone.width shadow.spreadRadius) / zone.width; double yScale (zone.height shadow.spreadRadius) / zone.height; Matrix4 m4 Matrix4.identity(); m4.translate(zone.width / 2, zone.height / 2); m4.scale(xScale, yScale); m4.translate(-zone.width / 2, -zone.height / 2); canvas.drawPath(path.shift(shadow.offset).transform(m4.storage), shadowPainter); } } Paint whitePaint Paint()..color Colors.white; canvas.drawPath(path, whitePaint); }这样只要指定路径和BoxShadow列表即可绘制出路径的阴影。// 第一个 drawShadows(canvas, rect, [ BoxShadow( color: Colors.black.withOpacity(0.1), offset: const Offset(0, 2), blurRadius: 6, spreadRadius: 0, ) ]); // 第二个 drawShadows(canvas, rect, [ BoxShadow( color: Colors.redAccent.withOpacity(0.2), offset: const Offset(0, 2), blurRadius: 8, spreadRadius: 0, ) ]); // 第三个 drawShadows(canvas, rect, [ BoxShadow( color: Colors.black.withOpacity(0.08), offset: const Offset(0,6), blurRadius: 16, spreadRadius: 0, ), BoxShadow( color: Colors.black.withOpacity(0.12), offset: const Offset(0,3), blurRadius: 6, spreadRadius: -4, ), BoxShadow( color: Colors.black.withOpacity(0.05), offset: const Offset(0,9), blurRadius: 28, spreadRadius: 8, ) ]);这样不止是矩形区域对于任何路径来说都可以实现阴影效果。这种阴影可以设置偏移、模糊半径、扩散半径也可以设置多阴影如下的第二片雪花是两个阴影的叠加。另外雪花路径的绘制在之前视频中介绍过这里就不贴代码了详见源码 shadow_painter_v2。drawShadows( canvas, path, [BoxShadow( color: Colors.blue.withOpacity(0.2), offset: const Offset(0, 2), blurRadius: 6, spreadRadius: 0, ), BoxShadow( color: Colors.redAccent.withOpacity(0.2), offset: const Offset(0, -2), blurRadius: 6, spreadRadius: 0, ) ]);本文我们探索了 Flutter 绘制中如何用 Canvas 绘制复杂的阴影样式。期间通过 BoxDecoration 在源码中的绘制逻辑发现其本质是通过模糊遮罩来实现阴影效果。并借此思路对 Path 的阴影绘制进行加强使其可以根据BoxShadow列表绘制阴影。那本文就到这里以后还会带来更多关于 Flutter 绘制的知识欢迎多多关注和支持下次再见 ~作者张风捷特烈链接https://juejin.cn/post/7194250504518500409最后如果想要成为架构师或想突破20~30K薪资范畴那就不要局限在编码业务要会选型、扩展提升编程思维。此外良好的职业规划也很重要学习的习惯很重要但是最重要的还是要能持之以恒任何不能坚持落实的计划都是空谈。如果你没有方向这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》帮大家将杂乱、零散、碎片化的知识进行体系化的整理让大家系统而高效地掌握Android开发的各个知识点。相对于我们平时看的碎片化内容这份笔记的知识点更系统化更容易理解和记忆是严格按照知识体系编排的。全套视频资料一、面试合集二、源码解析合集三、开源框架合集欢迎大家一键三连支持若需要文中资料直接点击文末CSDN官方认证微信卡片免费领取↓↓↓
返回列表