热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

flutteroffset_【译】Flutter中的花式背景动画

原文链接:https:medium.comfelixblaschkefancy-background-animations-in-flutter-4163d50f5c
原文链接:https://medium.com/@felixblaschke/fancy-background-animations-in-flutter-4163d50f5c37
本文主要介绍如何使用 simple_animations 实现漂亮的动画效果,Demo 可见 :gsy_flutter_demo

这篇文章将会介绍一个很有意思的动画效果,它能让 Flutter 的页面显得更加友好,同时本文也将展示如何使用 simple_animations 库,在 Flutter 上轻松地实现如下图所示的动画效果。

29a88fd74bed79d6ef3c0ebaee89ad73.gif

动画所需要展示的效果是:由平滑过渡的渐变背景组成,并且在文字下面会有多个波从右向左滑动。

接下来首先从背景渐变开始介绍,在 Flutter 中内置的 BoxDecoration 就支持使用 LinearGradient 来实现渐变效果,代码如下所示:

return Container(decoration: BoxDecoration(gradient: LinearGradient(begin: Alignment.topCenter,end: Alignment.bottomCenter,colors: [color1, color2])),
);

所以我们只需要在这个基础上去设置动画即可,这里直接使用 simple_animations 来实现效果, 在 simple_animations 中我们可以使用这两个对象来实现效果:

  • MultiTrackTween (动画处理对象,一次安排多个补间动画的属性)
  • ControlledAnimation(一个非常简单的基于补间动画的控件对象)

关于波形的实现在后面的文章中会介绍,这里先背景渐变的代码:

class AnimatedBackground extends StatelessWidget {@overrideWidget build(BuildContext context) {final tween = MultiTrackTween([Track("color1").add(Duration(seconds: 3),ColorTween(begin: Color(0xffD38312), end: Colors.lightBlue.shade900)),Track("color2").add(Duration(seconds: 3),ColorTween(begin: Color(0xffA83279), end: Colors.blue.shade600))]);return ControlledAnimation(playback: Playback.MIRROR,tween: tween,duration: tween.duration,builder: (context, animation) {return Container(decoration: BoxDecoration(gradient: LinearGradient(begin: Alignment.topCenter,end: Alignment.bottomCenter,colors: [animation["color1"], animation["color2"]])),);},);}
}

如上代码所示,在这里这里我们仅仅定义两个的 Track 的颜色 color1 和 color2,并将它设置到 ControlledAnimation 中,最后在 LinearGradient 使用这两个颜色。

是不是看起来很简单,这里甚至都没有看到任何 StatefulWidgetAnimationController,你可以将这段代码作为模板,并使用更复杂的颜色过渡对其进行扩展。

03d5aed79eb110d71fbc5047b57c2fb2.gif

现在我们有了一个渐变背景的动画,接着可以添加一些新的动画效果来完善效果,如下图所示是我们想要实现的最终效果:

443e55a2fe77c969999d23793ec25506.png

实际上,这是三个波型相互重叠的效果,这里我们需要确保它们彼此独立,以便于最终可以产生波纹的叠加效果。

因此,我们需要定义一个 WaveAnimation 具备以下属性的小控件:

  • speed:控制波浪动画的持续时间;
  • height:设置波浪作用的区域;
  • offset:x轴的偏移,以给出不同的波形“起始位置”;

接下来我们先要讨论一个数学的问题,要如何实现一个周期性的循环弧形动画效果呢?答案只有一个:三角函数

首先我们需要为动画设置一个 0.0 到 2 * pi 之间的值,并将该值放入到正弦函数中,接着我们在三个位置上采样 y 的数值大小:左侧、中间和末端。这样从左到右就覆盖了一个 pi 大小的间隔,因此我们始终可以看到一个完整的正弦波的一半。

我们为什么要采样三个位置?因为我们会根据这三个位置画一段 Path,如下图所示提供了这个可视化效果:

724c81c1e463fce9620637629ddce611.gif

我们从左上角开始(紫色),并在右上角(红色)添加一个二次贝塞尔函数连接过去,然后我们可以通过指定一个“控制点”(绿色)来实现这个变化,最后利用了 Flutter 的 Canvas 路径绘制的方法绘制出一个 Path

然后我们让红色的点往橙色移动,之后让紫色的点往黄色点以后,最后我们只需要把这个 Path 路径给连接起来就可以了。

当你只仅关注紫色、绿色和红色点的时候,就可以看到我们的采样后的路径是一个正弦波的效果。

这个二次贝塞尔函数乍一看似乎有些诡异,但是它只是想画一条从紫色到红色的直线(蓝色)。它和绿点之间的距离越长,线条就会受到某种类似重力因素的影响得到灰色的形状,最终的结果是线逐渐弯曲。

class AnimatedWave extends StatelessWidget {final double height;final double speed;final double offset;AnimatedWave({this.height, this.speed, this.offset = 0.0});@overrideWidget build(BuildContext context) {return LayoutBuilder(builder: (context, constraints) {return Container(height: height,width: constraints.biggest.width,child: ControlledAnimation(playback: Playback.LOOP,duration: Duration(milliseconds: (5000 / speed).round()),tween: Tween(begin: 0.0, end: 2 * pi),builder: (context, value) {return CustomPaint(foregroundPainter: CurvePainter(value + offset),);}),);});}
}class CurvePainter extends CustomPainter {final double value;CurvePainter(this.value);@overridevoid paint(Canvas canvas, Size size) {final white = Paint()..color = Colors.white.withAlpha(60);final path = Path();final y1 = sin(value);final y2 = sin(value + pi / 2);final y3 = sin(value + pi);final startPointY = size.height * (0.5 + 0.4 * y1);final controlPointY = size.height * (0.5 + 0.4 * y2);final endPointY = size.height * (0.5 + 0.4 * y3);path.moveTo(size.width * 0, startPointY);path.quadraticBezierTo(size.width * 0.5, controlPointY, size.width, endPointY);path.lineTo(size.width, size.height);path.lineTo(0, size.height);path.close();canvas.drawPath(path, white);}@overridebool shouldRepaint(CustomPainter oldDelegate) {return true;}
}

对于这个控件,我们使用了一个 LayoutBuilder 来检查可用的宽度再进行绘制,然后使用 ControlledAnimationPlayback.LOOP 实现从 0.0 到 2 * pi 的简单补间数据,之后可以将当前动画值传递到 CustomPainterCanvas 进行动画绘制。

最终这个 CustomPainter 可以实现我们想要的波形路径,但需要注意的是,我们使用的波形与不透明度需要一层层减小,这样多个波始重叠才能始终可见。

是不是用相当少的代码就实现了很炫酷的动画?

最后如下代码所示,我们使用 Stack 将控件堆叠在一起。

class FancyBackgroundApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return Stack(children: [Positioned.fill(child: AnimatedBackground()),onBottom(AnimatedWave(height: 180,speed: 1.0,)),onBottom(AnimatedWave(height: 120,speed: 0.9,offset: pi,)),onBottom(AnimatedWave(height: 220,speed: 1.2,offset: pi / 2,)),Positioned.fill(child: CenteredText()),],);}onBottom(Widget child) => Positioned.fill(child: Align(alignment: Alignment.bottomCenter,child: child,),);
}

b3f5437f93b93c4ac0ed9c273453cd2e.png

Flutter 文章汇总地址:

Flutter 完整实战实战系列文章专栏
Flutter 番外的世界系列文章专栏

资源推荐

  • 本文 Demo 可见 :gsy_flutter_demo/anim_bg_demo_page.dart
  • Github : https://github.com/CarGuo
  • 开源 Flutter 完整项目:https://github.com/CarGuo/GSYGithubAppFlutter
  • 开源 Flutter 多案例学习型项目: https://github.com/CarGuo/GSYFlutterDemo
  • 开源 Fluttre 实战电子书项目:https://github.com/CarGuo/GSYFlutterBook
  • 开源 React Native 项目:https://github.com/CarGuo/GSYGithubApp



推荐阅读
author-avatar
丿艹约锭So灬幸福老师
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有