简介
一般指的是手机上60Hz频率的,那么如果需要动画流畅,则需要在1s中之内绘制60帧的组件,那么动画帧率超过1000ms/60=16 FPS,就基本能看了,超过 32 FPS就会感觉相对平滑,而超过 32 FPS,大多数人基本上就感受不到差别了。
AnimatedContainer
可以理解成带动画的组件,只需要设置其时间duration
,那么其属性就会执行相应的动画。
AnimatedContainer(
duration: Duration(milliseconds: 300),
width: 100,
height: 32,
child: Align(
alignment: Alignment.center,
child: ...,
),
decoration: BoxDecoration(...)
)
AnimatedSwitcher
不同控件的切换动画。
AnimatedSwitcher(
duration: Duration(seconds: 1),
child: Text(
'Hello',
key: UniqueKey(),
),
);
如果是同一个控件,但是还是需要动画的话,就需要UniqueKey()
;如果需要在控件切换时,多个动画形式组合,则:
AnimatedSwitcher(
duration: Duration(seconds: 1),
transitionBuilder: (child, animation) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: animation,
child: child,
),
);
},
child: Text(
'Hello',
key: UniqueKey(),
),
);
Curve
动画的曲线运动方式类型。Flutter官方文档
TweenAnimationBuilder
创建一个补间动画的builder
,需要返回一个Widget
。
TweenAnimationBuilder(
duration: Duration(seconds: 1),
tween: Tween(begin: 0, end: 1),
builder: (BuildContext context, dynamic value, Widget child) {
return Opacity(
opacity: value,
child: Container(
width: 300,
height: 300,
color: Colors.white,
),
);
},
)
tween
:指的是关键帧。builder
:指的是在关键帧中间创建补全帧。value
:指的是0-1之间的数值,就是tween
参数中定义的。
注意:Tween(begin: 0, end: 1)
中的数值是int``double
都是可以的。
如果需要和不同的动画组合使用,应该怎么办呢?
例如添加一个平移动画:
TweenAnimationBuilder(
duration: Duration(seconds: 1),
tween: Tween(begin: 100, end: 120),
builder: (BuildContext context, dynamic value, Widget child) {
return Container(
width: 300,
height: 300,
color: Colors.blue,
child: Center(
child: Transform.translate(
offset: Offset(10, -30),
child: Text('Hello', style: TextStyle(fontSize: value),),
),
),
);
},
)
计数器动画
Center(
child: Container(
width: 300,
height: 120,
child: TweenAnimationBuilder(
duration: Duration(seconds: 1),
tween: Tween(begin: 0.0, end: 10.0),
builder: (BuildContext context, dynamic value, Widget child) {
final whole = value ~/ 1;
final decimal = value - whole;
return Stack(
children: [
Positioned(
top: -100 * decimal,
child: Opacity(
opacity: 1.0 - decimal,
child: Text(
'$whole',
style: TextStyle(fontSize: 100),
),
),
),
Positioned(
top: 100 - 100 * decimal,
child: Opacity(
opacity: decimal,
child: Text(
'${whole + 1}',
style: TextStyle(fontSize: 100),
),
),
),
],
);
},
),
),
)
主要是builder
参数中返回方法,Positioned
组件的位置动画+Opacity
组件的透明度动画,这样就可以满足计数器增加或是减少时的对应的数字的运动改变动画。
封装成动画组件
class AnimatedCounter extends StatelessWidget {
final Duration duration;
final int value;
const AnimatedCounter({Key key, @required this.duration, @required this.value}) : super(key: key);
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder(
duration: Duration(seconds: 1),
tween: Tween(end: value),
builder: (BuildContext context, dynamic value, Widget child) {
final whole = value ~/ 1;
final decimal = value - whole;
return Stack(
children: [
Positioned(
top: -100 * decimal,
child: Opacity(
opacity: 1.0 - decimal,
child: Text(
'$whole',
style: TextStyle(fontSize: 100),
),
),
),
Positioned(
top: 100 - 100 * decimal,
child: Opacity(
opacity: decimal,
child: Text(
'${whole + 1}',
style: TextStyle(fontSize: 100),
),
),
),
],
);
},
);
}
}
AnimatedCounter
对象中需要传入Duration
和当前跳转的数字count
。
使用
Center(
child: Container(
width: 300,
height: 120,
child: AnimatedCounter(duration: Duration(seconds: 1), value: count),
),
)
当然这个方法中的count,是需要状态改变的,需要在setState
方法中改变的。
特别说明
动画在程序启动的时候第一次才会用到begin的状态,之后的执行动画只会参考结束时的时候为参考位置,并到达新的end状态之间的动画。