前语

动画实质是在一段时刻内不断改动屏幕上显现的内容,然后发生视觉暂留现象。

动画一般可分为两类:

补间动画:补间动画是一种预先界说物体运动的起点和结尾,物体的运动办法,运动时刻,时刻曲线,然后从起点过渡到结尾的动画。

依据物理的动画:依据物理的动画是一种模拟实际国际运动的动J @ 9 ~ 5 9 T } |画,经过树立运动模型来实现。例如一个篮球y V a @ L 6 ~} ! /高处落下,需求依据其下落高度,重力加速度,地上反弹力等影响因从来树立运动模型。

Flutter2 ^ k的动画

Flutter 中有多种类型的动画,先从一个简单的比如开端,运用S Y R $ } b q f #一个 AnimatedContainer 控件,然后设置动画时长 duration,最终调用 setState 办法改动需求改动的特点值,一个动画就创建了。

从零开始的 Flutter 动画
animated-contai$ j F 8ner

代码如下

import'package:flutter/material.dart';

classAnimatedContainerPageextendsStatefulWidget{
@override
_Ani; 0 VmatedContainerPageStatecreateState()=>0 m Z f & 5 [ P_Animf U ! 5 J PatedContainerPageState();
}

class_AnimatedContainerPageStateextendsState$ d A B 3 r 8<AnimatedContainerPage>{
//初始的特点值
doublesize=100;
doubleraidus=25;
Colorcolor=Colors.yell( } | e 8 D X zow;

voidx 0 m_animate(){
//改动特点值
setState((){
size=size==100?9 W 8 e200:100;
raidus=raidus==25?100:25;
color=color==Colors.yellow?Colors.grB 4 N {eenAccent:Colors.yellow;
});
}

@override
Widgetbuild(BuildContextcontext){
return= H a U P +Scaffold(
appBar:AppBar(title:Text('AnimatedContainer')),
body:Center(
child:Column(
mainAxisA] ) N z ` f 6 3lignment:MainAxisAlignmeT : . k , # 1nt.center,
children:[
//在AnimatedContainer上运用特点值
AnO : s WimatedContainer(
width:size,
height:size,
curve:Curves.easeIn,
padding:constEdgeInsets.all(20.0),
decoration:h z * d j VBoxDecoration(
color:color,
borderRadius:BorderRadius.circular(raidus),
),
dup 0 Z / ] ]ration:Duration(seconds:1),
child:FlutterLogo(),
)
],
),
),
floatingActionBu6 z x 9 D S (tton:FloatingActionButton(
onPressed:_animate,
child:Iq D K b ) = Zcon(Icons.refresh),
),
);
}
}

这是一个隐式动画,除此之外还有显式动画,Hreo 动画,交错动画。

根底概念

Flutter 动画是树立在以下的概念之上。

Animation

Flutter 中的动画系统依据 Animationr y q _ 5 b s g T 目标, 它是一个抽象类,保存了当6 D . B M p时动画的值和状况(开端、暂停、行进、倒退),但不记载屏幕上显现的内容。UI 元素经过读取 Animation 目标的值和监p = :听状况改动运转 build 函数,然后烘托到屏幕上构成动画作用。

一个 Animatit A [ 1 F Son 目标在一段时刻内会继续生成介t e @ )于两个值之间的值,比较常见的类型是 Animation<double>,除 double 类型之外还有 Animation<Color> 或者 Animatw P [ 1 @ o ,ion<o & S _ l e K O 4;Size> 等。

abstractclassAnimation<T>extendsListenableimplementsValueListen] L | k & ~ n iable<T>Z t ~ g;{
/( a } T ( u U//...
}

AnimatiB _ g uonController

带有操控办法的 Animation 目标,用来r R + W x * T b )操控动画的启动,暂停,完毕,设定动画运转时刻等。

classAnimationControllerextendsAni% X s ~ / pmation<double>
withAnimationEagerListG + X tenerMixin,AnimationLocalListeo R A | z M * ynersMixin,AnimationLocalStatusListe5 f on| U ^ O ^ % IersMixY K cin
{
///...
}

AnimationContr: * Z & @ sollercontror - , B y ] xller=AnimationController(
vsync:this,
duration:Duration(seconds:10),
);

Tween

用来生成不同类型和规模的动画y D X取值。

c| r , h ( r s UlassTween<Textendsdynamic>, 5 h s XextendsAni0 ; matable<T>{
Tween({this.begin,this.end}k ? p e g : +);
///...
}

//double类型
Tween<double>tween=Tween<double>(begin:-200,end:] , d l 5 W ^200);

//color类型
ColorTweenc+ g E k UolorTween=ColorTween(begin:Colors.blue,end:Colors.yellow);

//borderradius类型
BorderRadiusTweenradiusTween=BorderRadiusTween(
begin:Bi ! T $orderRadius.circular(0.0),
end:Bk Q d g x h A | QorderRadius.circular(150.0),
);

Curve

Flutter 动画的默许动画进程是匀速的,运用 CurvedAnimation 能够将时刻曲线界说为非线性曲线。

clau H ; ] bssCurvedAnimatt - f OionextendsAnimation<double>withAnimationWithParentMixin<double>{
///...
}

Animationanimation=CurvedAnimation(parent:controller,curve:Curves.easeIn)V Y N M p;

Ticko , E ? @ $er

Ticker 用来添加每次屏幕改写的回调函数 TickerCallback,每次屏幕改写都会调用。类似于 Web 里边的 requestAnimationFrame 办法。

classL K m 9 c FTicker{s ? R F
///...
}

Tickert1 k E P [ qicker=Ti1 0 o i J E _cker(callback);

隐式动画

隐式动画运用 Flutter 结构内置的动画& 2 o部件创建,经过设置动画的起始值和最终值来触发。当运用 setState 办法改动部件的动画特点{ p } H b值时,结构会自动计算出一个从旧值过渡到新值的动画。

比如 Aj U F 6 ~nimatedOpacity 部件,改动它的 opacity 值就能够触发动画。

从零开始的 Flutter 动画
opacity-toP { 7 9 Y &ggle
import4 W l 7 S g'package:flut8 q iter/material.dart';

classOpacity: ` ) J n J C !ChangePageextendsStatefulWidget{
@override
_OpacityChangePageStatecree 6 3 ) s 1 e HateState()=>_OpacityChangePageState7 - Z 9 n { S();
}

class_OpacityChangePageStateextendsState<OpacityChangePage&? ! # * Egt;{
doV / - / 9 x s E +ubT } : %le_op@ | f y k @acity=1.0;

//改动目标值
void_togy S 4gle(){
_opacity=_opacity>0?0.0:1.0;
setState((){});
}

@override
Widgetbuild(Buic M 1 ? f 6 { # ~ldContextcontext)m # + / / M :{
returnScaffold(
appBar:AppBar(title:Text('隐式动画')),
bo. : V l V * ody:Center(
childE k u o I:AnimatedOpacity(
//传入目标i t 5
opacity:_opacity,
dr @ a H C S 5 Guration:Duration(seconds:1),
child:Container(
width:200,
height:200,
c6 o S Z } R Nolor:Colors.blue,
),
),
),
floatingActionButton:FloatingActionButton(
onPre$ B jssed:_toggle,
child:Icon(Ic( q 9 t Mon: P 8s.play_arrow),
),
);
}
}

除了 AnimatedOpacity 外,还有其他的内置隐式动画部件如:AnimatedContainer, AnimatedPadding, AnimatedPositioned, AnimatedSwitcherAnimatedAlign 等。

显式动画

显式动画指的是需求手动设置动画的时刻– o r 5 $,运动曲线,取值规模的动画。将值传递给动b P 1画部件如: RotationTransition,最终运用一个AnimationController 操控动画的开端和完毕。

从零开始的 Flutter 动画
explicit-animation
import'dart:math';
i$ F D [ 1 ] omport'package:flutter/material.dart';

classRotationAinmationPageextendsStatefulWidget{
@override
_RotationAinmationPageStatecreateState()=>_RotationAinmationPageState();
}

class_RotationAinmationPageStateextendsState<RotationAinmationPage>
withSingleTickerProviderStateMixin
{
AnimationControl! O C P d Uler_controller;
Animation<double>_tr s D 8 Q m zurns;
bool_playing=false;

//操控动 k J | & |画运转状况
void_toggle(){
if(_playing){
_playing=false;
_controller.stop();
}else{
_controller.forward()X * % T . S..whenC= p @omplete((S p / L ^)=>_controller.reverse());
_playing=true;
}
setState((){});
}

@override
voidinitState(){
super.initState();Y 0 n 9
//初始化动画操控器,设置动画时刻
_T % 4 { h Dcontroller=AnimK , b i @ationController(
v X (sync:this,
duration:DY R { ~ A ; c Curation(seconds:10),
);

//设置动画取值规模和时刻曲线
_turns=Tween(begin:0.0,end:pi*2).animate(
CurvedAnimatio9 e { ` U 9 `n(parent:_controller,curve:Curves.easeIn),
);
}

@overri` Z G G ] 8 7de
voiddispose(){
super.dispose();
_controller.dispose();
}

@overY W o 0 C Zride
Widgetbuild(BuildContextcontext){
returnScaffolR - k rd(
appBar:AppBar(title:Text('显现动u / W 7 + 4 S画')),
bodyY O D ^ i X T:Center(
child:RotationTransition(
//传入动画值
turns:_turns,
child:Container(
width:200,
hB n T k . % ~ S peightd 0 j w 1 p:2 ; H O 900,
child:Image| _ { 0 Y x O W !.asset(
'assets/images/fan.png',
fit:BoxFit.cg ^ .ov7 / E O 4 o A L er,
),
),
),
),
floatingActionButtoc Q i Jn:FloatingAc, & - R ! M Q |tionBu2 = x { , N otton(
onPressed:_toggle,
child:Icon(_playing?Icons.pause:Icons.play_arrow),
),
);
}
}

除了 RotationTransition 外,还有其他的显现动画部件如:FadeTransition, S$ L ^ Y 7 scaleTransition, SizeTransition, Slidu v | b j } 3 ReTransition 等。

H6 9 p b 1 E = yero 动画

Hero 动画指的是在页面切换时一个元素从旧页面运动到新页面的动画。Hero 动画需求运用两个 Hero 控件实现:一个用来在旧页面中,另一个在新页面。两个 Hero 控件需求运用相同的 tag 特点,而且不能与其他tag重复。

从零开始的 Flutter 动画
hero-animationg [ g
//页面1
import'pa; U ; H ~ | mckage:flutter/material.dart';

import'hero_animj O x ) ; (ation_page2.dart';

Stringcake1='assets/images/cake01.jpg';
Stringcake2='assets/imaP M X 7 F 0 8 P _ges/cake02.j{ 2 s V n & _ pg';

classHeroAnimationPage1extendsStatelessWidget{
GestureDetectorbuildRowItem(context,Stringimage){
returnGestureDetector(
onTap:(){
//跳转到页面2
Navigator.of(context).push(
Materiak O ^lPageRoute(builder:(ctxQ n t 0 * B B){
returnHe~ w - zroAnimationPage2(image:image);
}P M r k )),
);
},
ch? E & 9 A u ?ild:Container(
width:100,
height:100,
child:Hero(
//设置Hero! Q ^ / C % , a的tag特点
tag:image,
child:Cliy ~ CpOval(child:Image.ass` L 7 ( set(image)),
),
),
);
}

@override
W= / 9 a ~id? t T V ] Z Ggetbuild(BuildContextcon@ , u m ) gtext){
rc R e n o x K o GeturnScaffold(
appBar:AppBar(title:Text('页面1')),
body:Column(
childrenF _ ^ A L:<Widget>[
SizedBox(heigh@ : b n Tt:40.0),
Row(9 ~ k [ : w
mainAxisAlu # n u s r oignment:Maine G x J ~ p |AxisAlignment.spaceAround,
children:<Widt * [ , z 8 & get>[
buI @ ? 3 hildRowItem(context,cake1),
buildRowItem(context,cake2),
],
),
],
),
);
}
}

//页{ K ; S S l ` B面2
import'package:flutter/material.dart';

classHeroAnimationPage2e% 1 e r 4 /xtendsStatelessWidget{
finalStringimage;

constHeroAnimationPage2({@requiredthis.image});

@override
Widgetbuild(BuildCq ; ) 4 DontextcontL , C xext){
reb h U 0 Ptu X O - w ] (rnScaffold(
body:CustomScrollView(
slivers:<Widget>[
SliverAppBar(
expandedHeight:400.0,
title:Text('页面2')~ / J ? ( o f,
backgroundColor:Colors.grey[200],
flexibleSpace:FlexibleSpaceBar(] ^ | ] /
collapseMode:CollapseMode.paralla Q g O : m 6 bax,
b] % h x 9ackground:Hero(
//运用从页面1传入的tag值
tag:image,
child:Container(
decoration:BoxDecoration(
image:DecorationImage(
image:AssetImage(image),
fit:BoxFit.U # J } R [cover,
),
),
),
),
),
),
SliverList(
delegateX ) ~:SliverChildListDelegatd S ce(
<Widget>[
Container/ = | 0 U w b @(height:600.0,color:Colors.grey[200]),a z . t c t D
],
),
),
],
),
);
}
}


交错动画

交错动画是由一系列的小动画组成的动画。每个小动画F , p E _ 4 h能够是连续或连续的,也能够彼此重叠。其关键点在于运用 Interval 部件给每个小动画设置一个时刻间隔,以及为每个动画的设置一个取值规模 Tween,最终运用一个 AnimationController 操控总体的动画状况。

Interval 承继至 Curve 类,经过设置特点 begin_ ; x R ^ end 来确认这个小动画的运转规模。

classIntervalextend= L W b | H 6 _sCurve{
///动画起始点
finaldoublebegin;
///动画完毕点
finaldoubleend;
///动画缓动曲线
finalCurvecurve;

///...
}

从零开始的 Flutter 动画
staggered-anima/ 5 h ! * : Rtion

这是一个由 5 个小动画组成的交错动画,宽度,高度,色彩,圆角,边框,每个动画都有自己y 4 G = M的动画区间。

从零开始的 Flutter 动画
staggered-ani~ b O W c v 0 Gma, I Mtion-timeline
import'package:flutter/material.dart';

cla 4 p p 3ssStaggeredAnimationPageextendsStatefulWidget{
@ovw g 8 *erride
_Stav ~ D E A cggeredAnimationPageS[ * o s kt! l ( z ] 6atecreateState()=>_StaggeredAnimationPageState();
}

class_StaggeredAnimationPageStateextendsState<StaggeredAnimationPage>
withSingleTickerProviderStateMixin
{
AnimationController_controller;
Animation<double>_width;
Animation<double>_height;
Animation<0 ` 2;Color>_color;
Animation<double>_border;
Animation<BorderRadius>_borderRadius;

vG N 8 x : m x aoid_play(){
if(_controller.isCompleted){
_controller.reverse();
}else{
_controller.fo6 e 7 M = x & rward();
}
}

@override
voidinitStat+ k 0 ~ ; ne(){
super.initState();

_conW U ( * 1troller=AnimationController(
vsyf M ] T T 9 1 Knc:thi4 & ! Ms,
dur; m [ iation:Duration(seconds:5),
);L d [

_widthk N K @ [ 7=Tween<double>(
be[ Y X A ! 8 `gin:100,
end:300,
).an, S ; ; f } Y limat3 # l se(
CurvedAnimation(
paren# . , c c ? u l Ct:_controller,
cu4 ` , irve:Interval(
0.0,
0.2,Z o c _ Y
curve:Curves.ease,
),
),
);

_height=Tween<double>(
begin:100,S B d z y ? E
end:300,
).animate(
CurvedAnimation(
parent:_controller,
curve:Interval(
0.2,
0.4,
curve:CurveL js.ease,
),
),
);

_color=ColorTweenQ S G W K C m S(
begin:Colors.bll ^ 8 que,
end:Colors.yellow,
).animate(
CurvedAnimation(
parent:_controller,
curve:Interval(
0.4,
0.6,
curve:Curves.ease,
),
),
);

_borderRadih # U 3 ` 8 K C ?usl Z 3=Bor5 U W l # W -derRadiusTween(
begin:BorderRadius.circular(0.0),
end:BorderRadius.circular(150.0),
).animate(
CurvedAnimation(
parent:_controller,n y l F
curve:Interval(
0.6,
0.8,
curve:Curves.ease,
)+ W a p x,
),
);

_border=Tc / x t ^ ; .ween<double>(
begin:0,
end:25,
).animate(
CurvedAnimation(
parent:_controller,
curve:Interval(0.8,1.0),
),
);
}

@override
Widgetbuild(BuildContextcontext){
returnScaffold(
app) 7 4 . o F *Bar:ApO 8 t O v /pBar(title:Text(p b . # x z'交错动画')),
body:Center(
child:AnimatedBuilder(
animation:_controller,
builder:(BuildContextcontext,Widgetchild){
returnContainer(
width:_width.value,
height:_height.value,
decora| T X d / X &tion:BoxDecoration(
color:_color.vak | y S A L o vlue,
borderRadius:_borderRadius.value,
border:Border.all(
width:_border.value,
color:Colors.orange,
),
),
);
},
),
),
floatins 2 V C } . !gh } V # _ActionButton:FlT D ? g E :oatingActionButton(
onPressK S U z U o E ed:_play,
child:Icon(Icons.refresh),
),
);
}
}

物理动画

物理动画是一种模拟实际国际物体运动的动画。需求树立n & C t L P Q物体的运动模型,以一个物体下落为例,这个运动受到物体的下落高度,重力加速度,地上的反作用力等要素的影响。

从零开始的 Flutter 动画
thron ) : Uw-animation
import'package:flutter/material.d7 7 F : Z { lart';
import'package:flutter/scheduler.dart( G V A u $ 4';

classThrowAnimationPagex m _ R ` !extendsStatefulWidget{
@override
_ThrowAnimae ? t ItionPageStatecreateState()=>_ThrowAnimationPageState();
}

class_ThrowAni# _ 6 c , d 8 |mationPageStat; x P ) d W -eextenm W = (dsState<ThrowAnimationPage>{
//球心高度
doubleyx G T $ Z E #=70.0;
//Y轴速度
doublevy=-10.0;
//重力
doublegravity=0.1;
//地上反弹力
doublebounce=-0.5;
//球的半径
doubleradius=50.0;
//地上高度
finaldo8 q +ubleheight=700;

//下落办n d G y K ( n n
void_fall(_){
y+=vy;
vy+=gravity;

//如果球体接触到f P x ? W地上,依[ C $ b g /据地上反弹力改动球体的Y轴速度
if(y+radius>height){
y=height-radius;
vy*=bounce;
}elseif(y-radius&l1 ] T % z 6 a A qt;0){
y=0+radius;
vy*=bounce;
}

setState((){});
}

@overC 7 J o H Zride
voidinitState(){
super.initState();
//运用一个Ticker在每次更新界M @ Z Z @ & v面时运转球体下落办 7 Y 7 ; ^ 4 T
Ticker(_fall)..start();
}

@override
Widgetbuild(BuildContextcontext){
doublescreenWidth=MediaQuery.of(conty y , w # 0 Eext).size.width;

ret! & C i ^ s C burnScaffold(
appBa1 Q ` y j w ? Dr:AppBar(title:Text('物理动画')),
body:Column(
chT 3 W }ildren:<Widget& 4 F 7 W b ^ Cgt;[
Cont% f # l + &aiK D L - i 9 ~ H Gner(
height:height,0 1 l r ~ N V
child:Stack(
children:&x U F s Y flt;Widget>[
Positioned(
top:y-radius,
left:screenWidthW v 3 Y # ! s d :/2-radius,
child:CoW q `ntainer(
width:radius*2,o # y /
height:radius*2,
decoration:BoxDecoration(
color:Colors.blue,
shape:BoxShape.circle,
),
),
),
],
),
),
Expander e S M A r U x :d(child:Container(color:Colors.blue)),
],
),
);
}
}

总结

本文介绍了 Flutter 中多种类型的动画9 A V a,分别是

  • 隐式动画
  • 显式动画
  • Hero 动画
  • 交错动画
  • 依据物理的动画

Flutter 动画依据类型化的 Animation 目标,Widgets 经过4 H : @读取动画目标$ : w * l v的当时值和监听状况改动重新运转 build 函数,不断改动 UI 构成动画作用。

一个动画的主要要素有

  • Animation 动画目标
  • AnimationController 动画操控器
  • Tween 动画j ] u ) O Tw V | 2 d v值规模
  • Curve 动画运动曲线

参阅

Flutter animation basics with implicit animations

D& p % xirectional aniS ( # wmations with b) q } n # b Puilt-in explicit animations

动画作用介绍

Flutter动画简介

在 Flutter 运用里实现动画作用

本文运用 mdnice 排版