继续创造,加速成长!这是我参与「日新计划 10 月更文挑战」的第28天,点击检查活动概况
概述
除Hero, AnimatedWidget外,还有很多动画作用,如组件过度动画,物理动画,隐式动画,显式动画,交错动画等。但实质都是在一段时刻内不断改动屏幕上显现的内容,然后产生视觉暂留现象。
动画一般可分为两类:
[补间动画]:补间动画是一种预先界说物体运动的起点和结尾,物体的运动方式,运动时刻,时刻曲线,然后从起点过渡到结尾的动画。
「基于物理的动画」:基于物理的动画是一种模仿实际国际运动的动画,经过树立运动模型来完成。例如一个篮球 从高处落下,需求依据其下落高度,重力加速度,地上反弹力等影响因素来树立运动模型。
隐式动画
隐式动画运用 Flutter 结构内置的动画部件创立,经过设置动画的起始值和最终值来触发。当运用 setState
办法改动部件的动画特点值时,结构会主动计算出一个从旧值过渡到新值的动画。
比如 AnimatedOpacity
部件,改动它的 opacity
值就能够触发动画。
代码:
class OpacityChangePage extends StatefulWidget {
const OpacityChangePage({Key? key}) : super(key: key);
@override
State<OpacityChangePage> createState() => _OpacityChangePageState();
}
class _OpacityChangePageState extends State<OpacityChangePage> {
double _opacity = 1.0;
//改动目标值
void _toggle(){
_opacity = _opacity >0 ?0.0:1.0;
setState(() {
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: getAppBar("隐式动画"),
body: Center(
child: AnimatedOpacity(opacity: _opacity, duration: Duration(seconds: 2),
child: Container(width: 200,height: 200,color: Colors.yellow,),
)
),
floatingActionButton: FloatingActionButton(onPressed: _toggle,child: Icon(Icons.play_arrow),),
);
}
}
显式动画
显式动画指的是需求手动设置动画的时刻,运动曲线,取值规模的动画。将值传递给动画部件如: RotationTransition
,最后运用一个AnimationController
操控动画的开端和完毕。
代码:
import 'dart:math';
import 'package:demo202112/utils/common_appbar.dart';
import 'package:flutter/material.dart';
/// @Author wywinstonwy
/// @Date 2022/10/25 09:03
/// @Description:
class RotationAnimationPage extends StatefulWidget {
const RotationAnimationPage({Key? key}) : super(key: key);
@override
State<RotationAnimationPage> createState() => _RotationAnimationPageState();
}
class _RotationAnimationPageState extends State<RotationAnimationPage> with SingleTickerProviderStateMixin{
late AnimationController _controller;
late Animation<double> _turns;
bool _playing = false;
//操控动画状况
void _toggle(){
if(_playing){
_playing = false;
_controller.stop();
}else{
_controller.forward()..whenComplete(() => _controller.reverse());
_playing = true;
}
setState(() {
});
}
@override
void initState() {
// TODO: implement initState
super.initState();
//初始化动画操控器,设置动画时刻
_controller = AnimationController(vsync: this,duration: Duration(seconds: 10));
//设置动画取值规模和时刻曲线
_turns = Tween(begin: 0.0, end: pi * 2).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeIn),
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
_controller.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: getAppBar("显现动画"),
body: Center(
child: RotationTransition(
turns: _turns,
child: Container(
width: 200,
height: 200,
child: Image.asset("images/室内_电扇02.png",fit: BoxFit.cover,),
),
),
),
floatingActionButton: FloatingActionButton(onPressed: _toggle,
child: Icon(_playing ? Icons.pause : Icons.play_arrow),
),
);
}
}
除了 RotationTransition
外,还有其他的显现动画部件如:FadeTransition
, ScaleTransition
, SizeTransition
, SlideTransition
等。
交错动画
交错动画是由一系列的小动画组成的动画。每个小动画可所以接连或间断的,也能够相互重叠。其要害点在于运用 Interval
部件给每个小动画设置一个时刻距离,以及为每个动画的设置一个取值规模 Tween
,最后运用一个 AnimationController
操控整体的动画状况。
Interval
承继至 Curve
类,经过设置特点 begin
和 end
来确认这个小动画的运转规模。
class Interval extends Curve {
/// 动画起始点
final double begin;
/// 动画完毕点
final double end;
/// 动画缓动曲线
final Curve curve;
/// ...
}
这是一个由 5 个小动画组成的交错动画,宽度,高度,颜色,圆角,边框,每个动画都有自己的动画区间。
代码:
import 'package:demo202112/utils/common_appbar.dart';
import 'package:flutter/material.dart';
/// @Author wywinstonwy
/// @Date 2022/10/25 09:53
/// @Description:
class StaggeredAnimationPage extends StatefulWidget {
const StaggeredAnimationPage({Key? key}) : super(key: key);
@override
State<StaggeredAnimationPage> createState() => _StaggeredAnimationPageState();
}
class _StaggeredAnimationPageState extends State<StaggeredAnimationPage> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _width;
late Animation<double> _height;
late Animation<Color> _color;
late Animation<double> _border;
late Animation<BorderRadius> _borderRadius;
void _play(){
if(_controller.isCompleted){
_controller.reverse();
}else{
_controller.forward();
}
}
@override
void initState() {
// TODO: implement initState
super.initState();
_controller = AnimationController(vsync: this,
duration: Duration(seconds:5)
);
//宽度改动
_width = Tween<double>(begin: 100,end: 300).animate(CurvedAnimation(
parent: _controller,
curve:Interval(0.0, 0.2,curve: Curves.ease),
));
//高度改动
_height = Tween<double>(begin: 100,end: 300).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.2,0.4,curve: Curves.ease)
)
);
//颜色改动
_color = Tween(begin: Colors.blue,end: Colors.yellow).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.4,0.6,curve: Curves.ease)
)
) ;
_borderRadius = Tween(
begin: BorderRadius.circular(0.0),
end: BorderRadius.circular(150.0),
).animate(CurvedAnimation(parent: _controller,
curve: Interval(0.6,0.8,curve: Curves.ease)
)) ;
_border = Tween<double>(
begin: 0,
end: 25,
).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.8, 1.0),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('交错动画')),
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
return Container(
width: _width.value,
height: _height.value,
decoration: BoxDecoration(
color: _color.value,
borderRadius: _borderRadius.value,
border: Border.all(
width: _border.value,
color: Colors.cyanAccent
)
),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: _play,
child: Icon(Icons.refresh),
),
);
}
}
履行进程报错:Cannot lerp between “MaterialColor(primary value: Color(0xff2196f3))” and “MaterialColor(primary value: Color(0xffffeb3b))”.
To lerp colors, consider ColorTween instead.
物理动画
物理动画是一种模仿实际国际物体运动的动画。需求树立物体的运动模型,以一个物体下落为例,这个运动受到物体的下落高度,重力加速度,地上的反作用力等因素的影响。
代码:
import 'package:demo202112/utils/common_appbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
/// @Author wywinstonwy
/// @Date 2022/10/25 11:16
/// @Description:
class ThrowAnimationPage extends StatefulWidget {
const ThrowAnimationPage({Key? key}) : super(key: key);
@override
State<ThrowAnimationPage> createState() => _ThrowAnimationPageState();
}
class _ThrowAnimationPageState extends State<ThrowAnimationPage> {
// 球心高度
double y = 70.0;
// Y 轴速度
double vy = -10.0;
// 重力
double gravity = 0.1;
// 地上反弹力
double bounce = -0.5;
// 球的半径
double radius = 50.0;
// 地上高度
final double height = 700;
void _fall(_) {
y += vy;
vy += gravity;
//假如球体触及地上,依据地上反弹力改动球体的 Y 轴速度
if (y + radius > height) {
y = height - radius;
vy *= bounce;
} else if (y - radius < 0) {
y = 0 + radius;
vy *= bounce;
}
setState(() {});
}
@override
void initState() {
// TODO: implement initState
super.initState();
// 运用一个 Ticker 在每次更新界面时运转球体下落办法
Ticker(_fall)..start();
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(title: Text('物理动画')),
body: Column(
children: <Widget>[
Container(
height: height,
child: Stack(
children: <Widget>[
Positioned(
top: y - radius,
left: screenWidth / 2 - radius,
child: Container(
width: radius * 2,
height: radius * 2,
decoration: const BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
],
),
),
Expanded(child: Container(color: Colors.blue)),
],
),
);
}
}
组件过度动画
Widget特点发生改动时会履行过渡动画的组件统称为”动画过渡组件“,而动画过渡组件最显着的一个特征就是它会在内部自管理AnimationController。咱们知道,为了方便运用者能够自界说动画的曲线、履行时长、方向等,在前面介绍过的动画封装办法中,通常都需求运用者自己提供一个AnimationController目标来自界说这些特点值。可是,如此一来,运用者就必须得手动管理AnimationController,这又会添加运用的复杂性。因而,假如也能将AnimationController进行封装,则会大大提高动画组件的易用性。
咱们要完成一个AnimatedDecoratedBox
,它能够在decoration
特点发生改动时,从旧状况变成新状况的进程能够履行一个过渡动画。依据前面所学的常识,咱们完成了一个AnimatedDecoratedBox1
组件:
/// @Author wywinstonwy
/// @Date 2022/10/16 10:15 上午
/// @Description:
import "package:flutter/material.dart";
class AnimatedDecoratedBox1 extends StatefulWidget {
final BoxDecoration decoration;
final Widget child;
final Duration duration;
final Curve ?curve;
final Duration? reverseDuration;
const AnimatedDecoratedBox1({
required this.decoration,
required this.child,
required this.duration,
this.curve,
this.reverseDuration});
@override
_AnimatedDecoratedBox1State createState() => _AnimatedDecoratedBox1State();
}
class _AnimatedDecoratedBox1State extends State<AnimatedDecoratedBox1> with SingleTickerProviderStateMixin{
AnimationController get controller=>_controller;
late AnimationController _controller;
late Animation<double> _animation;
late DecorationTween _tween;
@override
void initState() {
// TODO: implement initState
super.initState();
_controller = AnimationController(
duration: widget.duration,
reverseDuration: widget.reverseDuration,
vsync: this
);
_tween = DecorationTween(begin: widget.decoration);
_updateCurve();
}
void _updateCurve(){
_animation = CurvedAnimation(parent: _controller, curve: widget.curve!);
}
@override
void didUpdateWidget(covariant AnimatedDecoratedBox1 oldWidget) {
super.didUpdateWidget(oldWidget);
if(widget.curve != oldWidget) _updateCurve();
_controller.duration = widget.duration;
_controller.reverseDuration = widget.reverseDuration;
//正在履行过度动画
if(widget.decoration !=(_tween.end??_tween.begin)){
_tween
..begin =_tween.evaluate(_animation)
..end = widget.decoration;
_controller
..value=0.0
..forward();
}
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
builder: (BuildContext context, Widget? child) {
return DecoratedBox(
decoration: _tween.animate(_animation).value);
},
animation: _animation,
child:widget.child,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
下面咱们来运用AnimatedDecoratedBox1
来完成按钮点击后背风光从蓝色过渡到红色的作用:
class MyExcessiveAnimation extends StatefulWidget {
const MyExcessiveAnimation({Key? key}) : super(key: key);
@override
_MyExcessiveAnimationState createState() => _MyExcessiveAnimationState();
}
class _MyExcessiveAnimationState extends State<MyExcessiveAnimation> {
Color _decorationColor = Colors.blue;
var duration = Duration(seconds: 1);
// Curve curve = CurvedAnimation(parent: ) as Curve;
// Tween doubleTween = Tween<double>(begin: -200.0, end: 0.0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: getAppBar("组件过度动画"),
body: Column(children: [
ElevatedButton(onPressed: (){
setState(() {
_decorationColor = Colors.red;
});
}, child: const Text('测试')),
Container(height: 44,
width: 200,
child: AnimatedDecoratedBox1(
decoration: BoxDecoration(color: _decorationColor),
child: ElevatedButton(onPressed: (){
setState(() {
_decorationColor = Colors.red;
});
},
child: const Text('AnimatedDecoratedBox',
style: TextStyle(color: Colors.black,fontSize: 14),
)
),
duration: duration,
curve: Curves.linear,
),
)
],)
,
);
}
}
总结
本文介绍了 Flutter 中多种类型的动画,分别是
- 隐式动画
- 显式动画
- 交错动画
- 基于物理的动画
- 组件过度动画
Flutter 动画基于类型化的 Animation
目标,Widgets
经过读取动画目标的当时值和监听状况改动重新运转 build
函数,不断改动 UI 形成动画作用。
一个动画的主要因素有
-
Animation
动画目标 -
AnimationController
动画操控器 -
Tween
动画取值规模 -
Curve
动画运动曲线
更多的动画能够参考官方文档:api.flutter.dev/flutter/ani…