Flutter 中的动画
Flutter 中动画的创建有很多种, 需要根据具体的需求选择不同的动画。如果只是简单的布局等的动画直接使用最简单的隐式动画就可以了,因为隐式动画是由框架控制的,所以仅仅只需要更改变需要变化属性就可以了。如果你想自己控制动画的变换则需要使用显示动画,如果需要控制一些列动画组合时使用交织动画去控制。如果内置的满足不了需求的时候,还可以结合画布自绘动画。
动画基础
Flutter动画和其他平台动画原理也是一样的,都是在快速更改UI实现动画效果。在一个Flutter动画中主要包含Animation(动画)、AnimationController(控制器)、Curve(速度曲线)、Animatable(动画取值范围)、Listeners (监听事件)、Ticker(帧)。
- Animation 一个抽象类是Flutter动画的核心类,主用于保存动画当前插值的和状态,在动画运行时会持续生成介于两个值之间的插入值。例如当宽从100变成200,会在动画第一帧到最后一帧都会生成100-200区间的一个值,如果速度是匀速的,这个值就是匀速增加到200。
- AnimationController 用来控制动画的状态启动、暂停、反向运行等, 是Animation的一个子类
- Curve 用来定义动画运动的是匀速运动还是匀加速等,和 css 中 animation-timing-function 类似
- Animatable 用于表明动画值范围值。可以通过调用animate方法,返回一个Animation,常见的Tween系列的类都是对他的实现
- Listener 监听动画状态的变化
- Ticker 帧回调,在动画执行时候每一帧都会调用其回调,类似与 js 中的 requestAnimationFrame
动画组成结构
动画选择
隐式动画
隐式动画简单来说就是我们只需要修改对应的属性,Flutter就是自己帮我们过渡动画,和css中过渡有点类似,当我们设置后transition后只需要更改对应的css属性就会自动过渡到新的值。Flutter 内置了一些常用的隐式动画,可以看到源码里都是对ImplicitlyAnimatedWidget的实现,如果需要我们也可以自己实现ImplicitlyAnimatedWidget来自定义隐式动画。
内置隐式动画
看个使用例子
// 首先我们在一个StatefulWidget定义 一个height和color
double heihgt = 100;
Color color = Colors.yellow[800];
// 在build 怎加一个隐式动画组建 AnimatedContainer,需要个Duration(动画执行时间),其他的参数和Container的基本一致
AnimatedContainer(
duration: Duration(milliseconds: 500),
height: heihgt, // 使用我们定义好的值
color: color,
margin: EdgeInsets.all(8),
child: Center(
child: Text('AnimatedContainer',
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
)
// 在需要执行动画时候我们修改 height 和 color 值,就会看到 上边的组建会一边变高边过渡到蓝色上
setState(() {
heihgt = 200;
color = Colors.blue;
});
在Flutter内置的隐式动画组件中,一般都是AnimatedXxxxxx类似的,后面的Xxxxxx都能找到对应的组件。内置的有下面这些 AnimatedContainer、AnimatedPadding、AnimatedAlign、AnimatedPositioned、AnimatedOpacity、SliverAnimatedOpacity、AnimatedDefaultTextStyle、AnimatedPhysicalModel。这些隐式动画的使用和其Xxxxxx对应的属性基本一致,只需要额外的指定 duration 就可以了,当然也可以为动画指定动画曲线 curve。
自定义隐式动画
当这内置的满足不了你的时候,你也可以去实现一个隐式动画,只需要实现抽象类 ImplicitlyAnimatedWidget。实现自定义隐式动画仅需要重写build 和 forEachTween 就可以简单实现了。
// 直接继承ImplicitlyAnimatedWidget
class AnimatedDemo extends ImplicitlyAnimatedWidget {
final Color color;
final Widget child;
final double height;
AnimatedDemo({
this.color,
this.height,
Curve curve = Curves.linear,
this.child,
@required Duration duration,
}) : super(curve: curve, duration: duration);
@override
_AnimatedDemo createState() => _AnimatedDemo();
}
//因为ImplicitlyAnimatedWidget是继承 StatefulWidget 的,所以还需要继承他的状态类 (AnimatedWidgetBaseState 继承自 ImplicitlyAnimatedWidgetState)
class _AnimatedDemo extends AnimatedWidgetBaseState<AnimatedDemo> {
ColorTween _color;
Tween<double> _height;
// 在动画执行时候会每一帧都调用 build
@override
Widget build(BuildContext context) {
return Container(
color: _color.evaluate(animation), //使用evaluate可以获取Tween当前帧的状态值
height: _height.evaluate(animation),
child: widget.child,
);
}
//首次build和更新时候会调用,在这里设置动画需要的Tween的开始值和结束值
@override
void forEachTween(visitor) {
//visitor 有三个参数(当前的tween,动画终止状态,一个回调函数(将第一次给定的值设置为Tween的开始值))
_color = visitor(_color, widget.color, (value) => ColorTween(begin: value));// 这里value==首次widget.color的值
_height = visitor(_height, widget.height, (value) => Tween<double>(begin: value));
}
}
我们可以去看 ImplicitlyAnimatedWidget 是如何控制动画的,在 ImplicitlyAnimatedWidgetState 中会看到其实里面定义了 AnimationController 控制动画。然后可以看到 didUpdateWidget 钩子函数中调用了 _controller.forward() 执行动画,当父 Widget 调用 setState 时候就会触发这个钩子函数的调用。
显示动画
有时候有些动画需要们自己去控制动画的状态,而不是交给框架去处理,这时就需要我们自己去定义前面简介里提到的那几个动画要素了。
内置显示动画
在Flutter中内置的显示动画大部分都是XxxxxxTransition名称的,我们看个内置显示动画使用例子,RotationTransition组件需要一个 turns(Animation<double>)参数,我们可以给它个AnimationController
// RotationTransition 参数
RotationTransition(
turns: Animation<double>,
child: ChildWidget(),
)
// AnimationController 参数
AnimationController(
double? value, // 初始值
this.duration, //动画时间
this.reverseDuration, // 反向动画执行的时间
this.debugLabel,
this.lowerBound = 0.0, //动画开始值
this.upperBound = 1.0, //动画结束值
this.animationBehavior = AnimationBehavior.normal,
required TickerProvider vsync, //垂直同步,需要一个 Ticker ,Flutter 给我们提供了
)
使用 RotationTransition,可以看到一个红蓝渐变色方块旋转一周。
class RotationTransitionDemo extends StatefulWidget {
@override
_RotationTransitionDemoState createState() => _RotationTransitionDemoState();
}
class _RotationTransitionDemoState extends State<RotationTransitionDemo> with SingleTickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
super.initState();
// 设置动画时间为1秒
_controller = AnimationController(duration: Duration(milliseconds: 1000), vsync: this)
..addListener(() { // 监听动画的状态值发生变化
print(_controller.value);
})
..addStatusListener((status) { //监听动画状态
// dismissed 动画在起始点停止
// forward 动画正在正向执行
// reverse 动画正在反向执行
// completed 动画在终点停止
print(status);
})
..forward(); // 执行动画
// 常用方法
// forward() // 正向执行动画
// reverse() 反向执行动画
// repeat() 重复执行 可以传个参数 是否会反向运动
// stop() 停止动画
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('RotationTransition'),
),
body: Center(
child: RotationTransition(
turns: _controller, // 设置 Animation
child: Container(
height: 300,
width: 300,
decoration: BoxDecoration(
gradient: LinearGradient(colors: [Colors.red, Colors.blue]),
),
),
),
),
);
}
}
控制器补间和曲线
在控制器中我们可以看的动画开始值和结束值默认是0.0到1.0,而且是double类型的。而实际动画中不可能只是double类型的,需要我们自己使用Animatable来指定补间范围值。
修改一下上面的代码
// 通过控制器的drive方法添加
_controller = AnimationController(duration: Duration(milliseconds: 1000),vsync: this)
..drive(Tween(begin: 1, end: 4)) //使用Tween(Animatable的子类)指定补间范围
// 我也也可以是使用Animatable的animate方法添加到控制器
Tween(begin: 1, end: 4).animate(_controller);
// 这样写我们可以使用 chain() 叠加多个 Tween
Tween(begin: 1, end: 4)
.chain(CurveTween(curve: Curves.ease)) //叠加个曲线
.animate(_controller);
Flutter已经内置帮我们实现了很多Animatable,ColorTween、SizeTween、IntTween、StepTween等等。
自定义显示动画
查看 RotationTransition 的源码,我们可以看到它是对的抽象类 AnimatedWidget 的实现,当内置的满足不了我们的时候,可以直接自己实现 AnimatedWidget 自定义显示动画。先来看看 AnimatedWidget 里面都有些啥。
// 只摘取主要的部分
abstract class AnimatedWidget extends StatefulWidget {
const AnimatedWidget({ Key key,@required this.listenable, }) : assert(listenable != null), super(key: key);
@override
_AnimatedState createState() => _AnimatedState();
}
class _AnimatedState extends State<AnimatedWidget> {
@override
void initState() {
super.initState();
widget.listenable.addListener(_handleChange);
}
void _handleChange() {
setState(() {
// 我们可以看到显示动画是通过控制器监听插值更改 setState 进行重绘。
});
}
}
接下来我自己继承 AnimatedWidget 实现一个自定义显示动画
// 继承 AnimatedWidget
class OpacityAnimatedWidget extends AnimatedWidget {
final Widget child;
Animation<Color> colorAnimation;
// AnimatedWidget 需要可传递一个 listenable 进去,我们可以传递个 AnimationController
OpacityAnimatedWidget(listenable, {this.colorAnimation, this.child}) : super(listenable: listenable);
@override
Widget build(BuildContext context) {
Animation<double> animation = listenable;
return Opacity(
opacity: animation.value,
child: Container(
color: colorAnimation.value,
child: child,
),
);
}
}
// 使用 需要在状态类上 混入一个 SingleTickerProviderStateMixin
AnimationController _controller = AnimationController(duration: Duration(milliseconds: 1000), vsync: this);
OpacityAnimatedWidget(
Tween(begin: 1.0, end: .8).animate(_controller),
colorAnimation: ColorTween(begin: Colors.red, end: Colors.blue).animate(_controller),
child: Container(
height: 300,
width: 300,
),
)
Flutter 内部还提供了一个 AnimatedBuilder 帮助我们简化自定义动画。
// 只需要三个三参数
AnimatedBuilder(
animation, // 一个listenable
child,// 传入个子组件,非必填
builder,// (BuildContext context, Widget child){} 这里的第二个参数 child ,就是上面传入的 child
// 这么做的好处就是,动画执行的时候只会执行 builder ,如果一个动画只是包裹层需要执行动画,这个时候就可以把包裹的子组件 放到外面传进去
// 这样就每次只需要 执行 builder 而方法第二个参数是传递进来的引用,所以可以避免每次都更新,减少开销
)
交织动画
官方是这么介绍的:交织动画是一个简单的概念:视觉变化是随着一系列的动作发生,而不是一次性的动作。动画可能是纯粹顺序的,一个改变随着一个改变发生,动画也可能是部分或者全部重叠的。动画也可能有间隙,没有变化发生。
简单点说就是一个动画可以分割成很多片段,每个片段都有不同的Tween,看个使用示例
class StaggeredAnimationDemo extends StatefulWidget {
@override
_StaggeredAnimationDemoState createState() => _StaggeredAnimationDemoState();
}
class _StaggeredAnimationDemoState extends State<StaggeredAnimationDemo> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _height;
Animation<Color> _color;
Animation<double> _borderRadius;
@override
void initState() {
super.initState();
_controller = AnimationController(duration: Duration(milliseconds: 5000), vsync: this);
_height = Tween(begin: 50.0, end: 300.0).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0, 0.15), // Interval 范围必须是0-1 指定Tween在哪一段时间执行
),
);
_color = ColorTween(begin: Colors.red, end: Colors.blue).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.1, 0.2),
),
);
_borderRadius = Tween(begin: 10.0, end: 150.0).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.1, 0.25),
),
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BasiceAppLayout(
title: '交织动画',
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Container(
height: _height.value,
width: _height.value,
decoration: BoxDecoration(
color: _color.value,
borderRadius: BorderRadius.circular(_borderRadius.value),
),
);
},
),
),
);
}
}
Hero动画
Flutter叫它主动画,用于不同页面之间切换时候动画,比如有一个商品列表,点击后跳到一个新的页面查看原图,就可以这个动画。使用也很简单,在不同页面使用Hero包裹需要动画组件,两个页面的 tag 需要甚至成一直,但是同一个页面需要保持唯一。
Hero(
tag: "avatar", //唯一标记,前后两个路由页Hero的tag必须相同
child: ChildWidget(),
)
Flutter 中的动画的更多相关文章
- 【Flutter 实战】动画序列、共享动画、路由动画
老孟导读:此篇文章是 Flutter 动画系列文章第四篇,本文介绍动画序列.共享动画.路由动画. 动画序列 Flutter中组合动画使用Interval,Interval继承自Curve,用法如下: ...
- 【Flutter 3-5】Flutter进阶教程——在Flutter中使用Lottie动画
作者 | 弗拉德 来源 | 弗拉德(公众号:fulade_me) Lottie动画 在移动开发中总是需要展示一些动画特效,作为程序员的我们并不是很擅长用代码做动画,即便是有些动画可以实现,在跨平台的过 ...
- Flutter中管理路由栈的方法和应用
原文地址:https://www.jianshu.com/p/5df089d360e4 本文首先讲的Flutter中的路由,然后主要讲下Flutter中栈管理的几种方法. 了解下Route和Navig ...
- 【Flutter 实战】动画核心
老孟导读:动画系统是任何一个UI框架的核心功能,也是开发者学习一个UI框架的重中之重,同时也是比较难掌握的一部分,下面我们就一层一层的揭开 Flutter 动画的面纱. 任何程序的动画原理都是一样的, ...
- CSS3中的动画效果记录
今天要记录的是CSS3中的三种属性transform.transition以及animation,这三个属性大大提升了css处理动画的能力. 一.Transform 变形 CSS中transform ...
- Android中矢量动画
Android中矢量动画 Android中用<path> 标签来创建SVG,就好比控制着一支画笔,从一点到一点,动一条线. <path> 标签 支持一下属性 M = (Mx, ...
- 初识android中的动画
动画效果可以大大提高界面的交互效果,因此,动画在移动开发中的应用场景较为普遍.掌握基本的动画效果在成熟的软件开发中不可或缺.除此之外,用户对于动画的接受程度远高于文字和图片,利用动画效果可以加深用户对 ...
- CSS3中的动画功能(一)
css3中的动画功能分为transitions功能和animations功能,这两种功能都可以通过改变css属性值来产生动画效果.今天带大家一起来看看css3动画功能中的transitions的用法. ...
- WPF中的动画——(三)时间线(TimeLine)
WPF中的动画——(三)时间线(TimeLine) 时间线(TimeLine)表示时间段. 它提供的属性可以让控制该时间段的长度.开始时间.重复次数.该时间段内时间进度的快慢等等.在WPF中内置了如下 ...
随机推荐
- flutter中ListView的详细讲解
1.ListView的简单介绍 ListView是最常用的可以滚动组件之一, 它可以沿一个方向进行线性排列所有的子组件. 下面是ListView的属性值介绍: scrollDirection:列表的滚 ...
- CRM系统选型时的参考哪些方面
企业不论在制定营销策略或是在进行CRM系统选型时,首先都是要了解自身的需求.每一家企业的情况和需求都有很大差异,CRM系统的功能也都各有偏重.有些CRM偏重销售管理.有些注重于营销自动化.有些则侧重于 ...
- WPF教程十二:了解自定义控件的基础和自定义无外观控件
这一篇本来想先写风格主题,主题切换.自定义配套的样式.但是最近加班.搬家.新租的房子打扫卫生,我家宝宝6月中旬要出生协调各种的事情,导致了最近精神状态不是很好,又没有看到我比较喜欢的主题风格去模仿的, ...
- Local dimming algorithm in matlab plus 1
(续)LED局部背光算法MATLAB仿真 在上一篇博客<Local dimming algorithm in matlab>中,我们实现了对一篇论文的算法用matlab仿真.在本篇论文中, ...
- 「BZOJ3545」「ONTAK2010」Peaks
「BZOJ3545」「ONTAK2010」Peaks 题目传送门 题目大意: 给定一个 \(n\) 个点,\(m\) 条边的带点权边权无向图,有 \(q\) 次询问,每次询问从 \(v\) 点出发,经 ...
- Java 给PDF签名时添加可信时间戳
一.程序运行环境 编译环境:IntelliJ IDEA 所需测试文件:PDF..pfx数字证书及密钥.PDF Jar包(Free Spire.PDF for Java).签名图片(.png格式) 可信 ...
- python04篇 文件操作(二)、集合
一.文件操作(二) 1.1 利用with来打开文件 # with open ,python 会自动关闭文件 with open('a.txt', encoding='utf-8') as f: # f ...
- 微信小程序云开发-云存储的应用-识别营业执照
一.准备工作 1.创建云函数identify 二.云函数identify中index.js代码 1 // 云函数入口文件 2 const cloud = require('wx-server-sdk' ...
- 为什么每次下载后必须关闭掉IO流(十五)
读一个文件,忘记关闭了流,你在操作系统里对这个文件的写,删除等操作就会报错,告诉你这个文件被某个进程占用,这是为什么呢? java是从c++设计来的,但是无论是C语言还是C++,都需要手动释放内存,j ...
- java 日期字符串互相转换
一.把日期转换成字符串 //获取当前时间 Date date = new Date(); //打印date数据类型 System.out.println(date.getClass().get ...