利用Android实现光影流动特效的方法详解

目录
  • 前言
  • MaskFilter 类简介
  • MaskFilter 的几种效果对比
  • 光影流动
    • 光影流动效果1
    • 光影流动效果2
    • 光影流动效果3
    • 光影流动效果4:光影沿贝塞尔曲线流动
  • 总结

前言

Flutter 的画笔类 Paint 提供了很多图形绘制的配置属性,来供我们绘制更丰富多彩的图形。前面几篇我们介绍了 shader 属性来绘制全屏渐变的聊天气泡背景、渐变流动的边框和毛玻璃效果的背景图片,具体可以参考下面几篇文章。

  • 让你的聊天气泡丰富多彩!
  • 手把手教你实现一个流动的渐变色边框
  • 利用光影变化构建立体旋转效果
  • Flutter 实现背景图片毛玻璃效果

本篇我们引入一个 Paint 类新的属性:maskFilter,再结合之前的 shader 和动画,看看能玩出什么花样。

MaskFilter 类简介

MaskFilter 类,顾名思义是遮罩过滤器,也就是在绘制过程中给图像加一层遮罩效果,这个遮罩效果是通过某种变换函数实现的。Flutter 官方文档的说明如下,可以看出其实就是对位图的颜色处理。

A mask filter to apply to shapes as they are painted. A mask filter is a function that takes a bitmap of color pixels, and returns another bitmap of color pixels. 遮罩过滤器应用于要绘制的形状,其实就是一个函数,接收一个彩色像素位图,然后返回另一个彩色像素位图。

在 Flutter 里面,目前只提供了一个 MaskFilter 的命名构造函数方式实例化,就是模糊效果。这个模糊效果和我们的图片模糊有点类似,也是使用高斯模糊,只是多了一个模糊样式参数。定义如下:

const MaskFilter.blur(
  this._style,
  this._sigma,
)

关于这个方法的说明,为了便于理解,将官方的文档翻译如下:

Creates a mask filter that takes the shape being drawn and blurs it. This is commonly used to approximate shadows. The style argument controls the kind of effect to draw; The sigma argument controls the size of the effect. It is the standard deviation of the Gaussian blur to apply. The value must be greater than zero. The sigma corresponds to very roughly half the radius of the effect in pixels. A blur is an expensive operation and should therefore be used sparingly. The arguments must not be null. 创建一个遮罩过滤器将要绘制的形状进行模糊处理。通常用于实现近似阴影的效果。style 参数控制绘制的效果类型 (BlurStyle 枚举);sigma 参数控制效果的尺寸,实际就是使用的高斯模糊的标准差。sigma 的值必须大于0,这个值在像素上,大致是效果范围的半径值。模糊处理比较耗性能,因此要有节制地使用。

因为效果有点类似阴影,而且比较耗性能,因此如果仅仅是要绘制阴影的话,官方推荐是使用 Canvas 的 drawShadow 方法替代这个效果。

MaskFilter 的几种效果对比

看文档使用起来比较简单,我们来看看 MaskFilter 的几种不同的模糊样式的区别。模糊样式通过 style 参数控制,BlurStyle 枚举取值有以下四种。

  • normal:形状内外都会做模糊处理,可以用于绘制图形表面投影的阴影效果。
  • solid:内部不模糊,外侧模糊,会让图形看起来更明亮,类似荧光的效果。
  • outer:外侧模糊,内部没有东西,适用于绘制半透明图形的阴影。
  • inner:外侧不处理,内部模糊,看起来有种内发光的效果。

看文档说明我们是体会不到具体的呈现效果的,我们绘制同一个图形的不同效果对比看看。

outer 类型有点奇怪,中间掏空了,不过看上去还挺酷的。我画了一个不模糊的叠加上去,发现组合后的效果就和 solid 模式一样。

上面绘制的代码如下。

@override
void paint(Canvas canvas, Size size) {
  var paint = Paint();
  paint.style = PaintingStyle.fill;
  var center = Offset(size.width / 2, size.height / 2);
  var radius = 80.0;
  paint.color = Colors.blue[400]!;
  paint.maskFilter = MaskFilter.blur(BlurStyle.outer, 20.0);
  canvas.drawCircle(center, radius, paint);
}

光影流动

通过绘制的效果发现,其实 MaskFilter 实现的效果有点像设计师给的各种发光效果,比如内发光、外发光。这时候,配合动画可以玩点有趣的东西了。

光影流动效果1

我们利用之前实现的全屏渐变色聊天气泡中的效果,通过动画控制一个圆形上下移动看看会有什么效果,感觉是一个彩色的光球在上升和下降。

上面效果的实现代码如下,基本的逻辑如下:

  • 整个绘图范围通过 shader 预填充,使得圆形移动过程的填充色渐变变化,和我们聊天气泡中的效果一样;
  • 通过 transform 动画控制颜色旋转,使得绘制的圆形的填充颜色旋转,看起来会有立体感;
  • 通过 maskFilter,设置为 solid 模式让圆形有荧光的效果;
  • 通过动画控制圆形上升和下降。
void _drawMovingCircle(Canvas canvas, Size size, Paint paint) {
  var radius = 80.0;
  paint.shader = LinearGradient(
    begin: Alignment.topCenter,
    end: Alignment.bottomCenter,
    colors: [
      Colors.black87,
      Colors.purple,
      Colors.blue,
      Colors.green,
      Colors.yellow[500]!,
      Colors.orange,
      Colors.red[400]!
    ].reversed.toList(),
    tileMode: TileMode.clamp,
    transform: GradientRotation(
      animationValue * 2 * pi,
    ),
  ).createShader(Offset(0, 0) & size);

  paint.maskFilter = MaskFilter.blur(BlurStyle.solid, 20.0);
  canvas.drawCircle(
      Offset(size.width / 2, size.height * animationValue), radius, paint);
}

光影流动效果2

上面的效果是一个圆形的,我们换成多个并排的矩形来看看,这种效果感觉整个填充区域的颜色在不停流动变幻,就好像有霓虹灯照耀的感觉。

上面效果的实现代码如下,其实就是通过循环绘制了一排矩形,然后通过动画控制上下移动位置。

void _drawMultiMovingRect(Canvas canvas, Size size, Paint paint) {
  paint.shader = LinearGradient(
    begin: Alignment.topCenter,
    end: Alignment.bottomCenter,
    colors: [
      Colors.black87,
      Colors.purple,
      Colors.blue,
      Colors.green,
      Colors.yellow[500]!,
      Colors.orange,
      Colors.red[400]!
    ].reversed.toList(),
    tileMode: TileMode.clamp,
    transform: GradientRotation(
      animationValue * 2 * pi,
    ),
  ).createShader(Offset(0, 0) & size);

  paint.maskFilter = MaskFilter.blur(BlurStyle.solid, 20.0);
  var count = 10;
  for (var i = 0; i < count + 1; ++i) {
    canvas.drawRect(
      Offset(size.width / count * i, size.height * animationValue) &
          Size(size.width / count, size.width / count * 2),
      paint,
    );
  }
}

光影流动效果3

这一次我们使用 outer 类型的模糊效果,然后让一串圆形沿屏幕对角线从收起到展开,再从展开到收起,效果如下所示,光束球发出来的时候,感觉就像是从左上角发了一个大招。

上面的实现代码如下所示,就是通过控制圆形的中心位置实现对角线移动的,间距则是随着动画值的增加而拉大,因此会有发射的效果。

void _drawMultiMovingCircle(Canvas canvas, Size size, Paint paint) {
  paint.shader = LinearGradient(
    begin: Alignment.topCenter,
    end: Alignment.bottomCenter,
    colors: [
      Colors.black87,
      Colors.purple,
      Colors.blue,
      Colors.green,
      Colors.yellow[500]!,
      Colors.orange,
      Colors.red[400]!
    ].reversed.toList(),
    tileMode: TileMode.clamp,
    transform: GradientRotation(
      animationValue * 2 * pi,
    ),
  ).createShader(Offset(0, 0) & size);

  paint.maskFilter = MaskFilter.blur(BlurStyle.outer, 20.0);
  var count = 10;
  for (var i = 0; i < count + 1; ++i) {
    canvas.drawCircle(
      Offset(size.width * i / count * animationValue,
          size.height * i / count * animationValue),
      size.width / count,
      paint,
    );
  }
}

光影流动效果4:光影沿贝塞尔曲线流动

我们来把图形通过贝塞尔曲线控制绘制位置,来画一组图形,就能够实现光影沿着贝塞尔曲线流动的效果了。这里我们沿着两条首尾相接的贝塞尔曲线,绘制了一组正方形,看起来就像光影在一个闭合的图形中来回穿梭一样。

实现代码和之前的类似,只是矩形的位置通过贝塞尔曲线生成,如下所示。

void _drawRectsUsingBezier(Canvas canvas, Size size, Paint paint) {
  paint.shader = LinearGradient(
    begin: Alignment.topCenter,
    end: Alignment.bottomCenter,
    colors: [
      Colors.black87,
      Colors.purple,
      Colors.blue,
      Colors.green,
      Colors.yellow[500]!,
      Colors.orange,
      Colors.red[400]!
    ].reversed.toList(),
    tileMode: TileMode.clamp,
    transform: GradientRotation(
      animationValue * 2 * pi,
    ),
  ).createShader(Offset(0, 0) & size);

  paint.maskFilter = MaskFilter.blur(BlurStyle.outer, 2);
  final height = 120.0;
  var p0 = Offset(0, size.height / 2 + height);
  var p1 = Offset(size.width / 4, size.height / 2 - height);
  var p2 = Offset(size.width * 3 / 4, size.height / 2 + height);
  var p3 = Offset(size.width, size.height / 2 - height);
  var count = 150;
  var squareSize = 20.0;
  for (var t = 1; t <= count; t += 1) {
    var curvePoint =
        BezierUtil.get3OrderBezierPoint(p0, p1, p2, p3, t / count);

    canvas.drawRect(
      curvePoint & Size(squareSize, squareSize),
      paint,
    );
  }

  for (var t = 1; t <= count; t += 1) {
    var curvePoint =
        BezierUtil.get3OrderBezierPoint(p3, p1, p2, p0, t / count);

    canvas.drawRect(
      curvePoint & Size(squareSize, squareSize),
      paint,
    );
  }
}

总结

本篇介绍了 Flutter 画笔类 PainterMaskFilter 的使用,MaskFilter 的应用效果就是让形状能有类似发光的效果。我们结合之前的几篇文章用到的绘制效果完成了一场“光影秀”,视觉上看起来还是很美的。这也是开发过程中绘图的乐趣之一,看着自己画出来很美的效果也还是很有成就感的。

到此这篇关于利用Android实现光影流动特效的方法详解的文章就介绍到这了,更多相关Android光影流动特效内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android实现流动的渐变色边框效果

    目录 前言 实现思路 总结 前言 记得在介绍 motion_toast 一篇的时候,开篇有一张动图,边框是渐变色而且感觉是流动的.这个动效挺有趣的,当时也有人问怎么实现,经过上一篇<让你的聊天气泡丰富多彩!>后,有了实现思路了. 实现思路 首先要实现但是渐变色边框,这个其实可以参考上一篇的CustomPaint 的渐变填充实现.绘制一个矩形边框,然后让渐变色的区域填充到矩形区域内就可以了. void paint(Canvas canvas, Size size) { final rectWid

  • Android实现好看的微信聊天气泡效果

    目录 前言 代码实现 踩坑记录 总结 前言 在聊天类应用中,通常用气泡作为聊天内容的背景色,比如微信的聊天背景,别人发过来的是白色的气泡,自己发的是绿色的气泡. 上面这种是比较普通的,这篇我们玩点有趣的,让聊天气泡是渐变色的.可能很多人会觉得渐变很简单,给 Container 来个decoration或者使用 DecoratedBox,使用渐变填充色就可以了,比如下面这种效果: 这个感觉也太丑了,本篇我们来一个高级的 —— 整个聊天窗口的气泡颜色是渐变的,而且随着滚动还会变化!先看看实现的效果,

  • 利用Flutter实现背景图片毛玻璃效果实例

    目录 前言 使用 canvas 绘制图片 更改绘制图片的绘制范围 毛玻璃效果实现 总结 前言 继续我们绘图相关篇章,这次我们来看看如何使用 CustomPaint 实现毛玻璃背景图效果.毛玻璃背景图其实就是将图片进行一定程度的模糊,背景图经过模糊后更加虚幻,使得前景和后景就会有层次感.相比直接加蒙层的效果来说,毛玻璃看起来更加好看一些.下面是背景图处理前后的对比,我们的前景图片的透明度并没有改变,但是背景图模糊虚化后,感觉前景更加显眼了一样. 本篇涉及如下内容: 使用 canvas 绘制图片.

  • Android实现翻页特效

    本文实例为大家分享了Android实现翻页特效的具体代码,供大家参考,具体内容如下 android-flip是一个能够轻松帮你实现水平以及竖直翻页特效的库,但是在判断翻页的时候有bug,我们需要在FlipCards.java中找到这一段: if (Math.abs(getPageIndexFromAngle(accumulatedAngle + angleDelta) - lastPageIndex) <= 1) {       accumulatedAngle += angleDelta;  

  • Android自定义View实现雪花特效

    本文实例为大家分享了Android自定义View实现雪花特效展示的具体代码,供大家参考,具体内容如下 效果图 1.SnowView 类 package com.ilz.rocketapplication.handaccount.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; impo

  • Android利用Flutter实现立体旋转效果

    目录 前言 ImageShader 简介 构建 ui.Image对象 使用 ImageShader 填充形状 立体旋转效果实现 总结 前言 之前我们提到了 CustomPaint er 的 Paint 可以使用渐变(GradientShader)来填充绘制的图形,本篇我们来介绍使用图片填充,并且配合动画实现“立体”旋转效果,之所以给“立体”加上引号,是因为实际是通过填充图片自身的光影效果旋转后看起来像是立体效果一样.下面是实现的效果图. ImageShader 简介 ImageShader 的定

  • 利用Android实现光影流动特效的方法详解

    目录 前言 MaskFilter 类简介 MaskFilter 的几种效果对比 光影流动 光影流动效果1 光影流动效果2 光影流动效果3 光影流动效果4:光影沿贝塞尔曲线流动 总结 前言 Flutter 的画笔类 Paint 提供了很多图形绘制的配置属性,来供我们绘制更丰富多彩的图形.前面几篇我们介绍了 shader 属性来绘制全屏渐变的聊天气泡背景.渐变流动的边框和毛玻璃效果的背景图片,具体可以参考下面几篇文章. 让你的聊天气泡丰富多彩! 手把手教你实现一个流动的渐变色边框 利用光影变化构建立

  • Android程序打包为APK的方法详解

    Andriod安装包文件(Android Package),简称APK,后缀名为.apk. 1.生成未签名的安装包 Build -> Build Bundle(s)/APK(s) -> Build APK(s)    会生成一个未签名的apk文件,默认为debug版,可以正常安装使用. 可以 Build -> Select Build Variant -> 选择生成的apk版本(debug.release),再 Build -> Build Bundle(s)/APK(s)

  • Android适配底部虚拟按键的方法详解

    最近项目进行适配的时候发现部分(如华为手机)存在底部虚拟按键的手机会因为虚拟按键的存在导致挡住部分界面,因为需要全屏显示,故调用虚拟按键隐藏方法使之隐藏,然而发现出现如下问题: 手动操作隐藏虚拟按键后出现长白条区域 不自动隐藏 滑出状态栏后虚拟按键也出来,状态栏隐藏后虚拟却不跟着隐藏 在没有虚拟按键的设备上影响了SurfaceView全屏显示图传(原本全屏显示的图传在切出去再进来时变成了小屏显示) 通过google了很多方法并尝试终于解决了这个问题,达到如下效果: 每次进入界面时虚拟按键自动隐藏

  • Android 通过代码安装 APK的方法详解

    在 APK 开发中,通过 Java 代码来打开系统的安装程序以安装 APK 并不是什么难事,一般的 Android 系统都有开放这一功能. 但随着 Android系统版本的迭代,其对于权限的把控越来越严格,或者说是变得越来越注重安全性.这就导致了以前可以通过很简单的几行代码就能实现的功能,现在要复杂很多. 对于通过代码打开系统安装程序这一功能的限制,其分水岭在 Android7.0,即 Android N 上.通常在 Android N以上的系统使用一种做法,以下则使用另一种做法. 传统的通过代

  • Android学习之Span的使用方法详解

    目录 Span集合 段落类Span 其他Span 展示效果 小试牛刀 小结 Span集合 段落类Span BulletSpan 为段落开头增加项目符号并支持大小.颜色.弧度 span.append(SpannableString("BulletSpan").also { it.setSpan(BulletSpan(40, Color.RED), 0, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) }) QuoteSpan 为段落开头增加垂直引用线 sp

  • Vue利用openlayers实现点击弹窗的方法详解

    目录 解释 编写弹窗 引入 openlayer使用弹窗组件 点击事件 这个写的稍微简单一点就行了,其实呢,这个不是很难,主要是知道原理就可以了. 我想实现的内容是什么意思呢?就是说页面上有很多坐标点,点击坐标点的时候在相应的位置弹出一个框,然后框里显示出这个坐标点的相关数据. 解释 这个内容的其实就是添加一个弹窗图层,然后在点击的时候让他显示出来罢了. 编写弹窗 首先一点,我们这个弹窗需要自己写一下,具体的样式,展示的内容之类的,所以说写一个弹窗组件,然后在openlayer文件中引用加载. 比

  • 利用Pytorch实现获取特征图的方法详解

    目录 简单加载官方预训练模型 图片预处理 提取单个特征图 提取多个特征图 简单加载官方预训练模型 torchvision.models预定义了很多公开的模型结构 如果pretrained参数设置为False,那么仅仅设定模型结构:如果设置为True,那么会启动一个下载流程,下载预训练参数 如果只想调用模型,不想训练,那么设置model.eval()和model.requires_grad_(False) 想查看模型参数可以使用modules和named_modules,其中named_modul

  • 利用Vue3实现可复制表格的方法详解

    目录 前言 最基础的表格封装 实现复制功能 处理表格中的不可复制元素 测试 前言 表格是前端非常常用的一个控件,但是每次都使用v-for指令手动绘制tr/th/td这些元素是非常麻烦的.同时,基础的 table 样式通常也是不满足需求的,因此一个好的表格封装就显得比较重要了. 最基础的表格封装 最基础基础的表格封装所要做的事情就是让用户只关注行和列的数据,而不需要关注 DOM 结构是怎样的,我们可以参考 AntDesign,columns dataSource 这两个属性是必不可少的,代码如下:

  • Android编程自定义AlertDialog样式的方法详解

    本文实例讲述了Android编程自定义AlertDialog样式的方法.分享给大家供大家参考,具体如下: 开发的时候,通常我们要自定义AlertDialog来满足我们的功能需求: 比如弹出对话框中可以输入信息,或者要展示且有选择功能的列表,或者要实现特定的UI风格等.那么我们可以通过以下方式来实现. 方法一:完全自定义AlertDialog的layout.如我们要实现有输入框的AlertDialog布局custom_dialog.xml: <?xml version="1.0"

  • Android使用jni调用c++/c方法详解

    1.下载ndk 2.编写jni的加载类 参考例子: public class JniTest { public native String append(String str1, String str2); static { System.loadLibrary("JniTest"); } } 以上append方法就是要调用c++/c中的方法. JniTest是在Android.mk里约束好的,关于Android.mk的编写具体在后面详解. 3.使用javah -jni生成.h文件 编

随机推荐