文章目录
一、动画
1.补间(Tween)动画
- 在补间动画中我们定义开始点和结束点、时间线以及定义转换时间和速度曲线。然后由系统计算,从开始点到结束点。从而形成动画效果
- 例如:透明度从0到1,颜色值从0到255
2.拟物动画
- 拟物动画是对真实世界的行为进行建模,使动画效果类似于现实中的物理效果
- 例如:弹簧,阻尼,重力,抛物线等
3.Animation
- Animation是Flutter动画库中的一个核心类。它包含动画的值和状态两个属性,定义了动画的一系列监听函数
- 监听值
- addListener
- removeListener
- 监听状态
- addStatusListener
- removeStatusListener
- 监听值
- 动画状态
-
AnimationStatus.dismissed(动画初始状态)
-
AnimationStatus.completed(动画结束状态)
-
AnimationStatus.forward(动画处在从开始到结束的运行状态)
-
AnimationStatus.reverse(动画处在从结束到开始的运行状态)
-
- AnimationController(动画控制器)
-
在指定时间内,将组织属性值由初始值演变到终止值。从而形成动画效果
-
参数
-
duration(动画的执行时间)
-
reverseDuration(动画反向执行时间)
-
lowerBound=0.0(动画最小值)
-
upperBound=1.0(动画最大值)
-
value(动画初始值,默认是lowerBound)
-
vsync(TickerProvider类型的对象,用来创建Ticker对象)
-
-
当创建一个AnimationController时,需要传递一个vsync参数
- vsync的作用是:防止屏幕外动画(动画页面切换到后台时)消耗不必要的资源
- 通过将SingleTickerProviderStateMixin添加到类定义,可以将stateful对象作为vsync的值
-
AnimationController具有控制动画的方法
-
.forward()-可以正向执行动画
-
.reverse()-可以反向执行动画
-
.dispose()-用来释放动画资源(在不使用时需要调用该方法,否则会造成资源泄露)
-
.stop()-用来停止动画执行
-
-
- Tween
-
AnimationController动画生产值的默认区间是0.0到0.1,如果希望使用不同的区间,或不同的数据类型,需要使用Tween
-
Tween的唯一职责就是定义从输入范围到输出范围的映射
-
Tween(begin,end)
-
ColorTween(begin:Colors.颜色,end:Colors.颜色)
-
- CurvedAnimation
- 简介
- 动画执行的速度有多种(匀速、先快后慢或先慢后快)这里的速度称为动画曲线
- CurvedAnimation的目的是为AnimationController添加动画曲线
- 组件
- CurvedAnimation(parent:controller,curve:Curves.easeIn)
-
parent(动画控制器对象)
-
curve(正向执行的动画曲线)
-
reverseCurve(反向执行的动画曲线)
-
- Curves
- CurvedAnimation(parent:controller,curve:Curves.easeIn)
- 简介
4.步骤
- 创建动画控制器
- controller = AnimationController(duration, vsync);
- 创建动画
- 动画曲线(CurvedAnimation)
- 补间动画(Tween)
- 监听动画
- addListener()-监听动画生产值
- addStatusListener()-监听动画状态
- 执行动画
- controller.forward()-正向执行
- controller.reverse()-反向执行
5.代码
class AnimationDemo extends StatefulWidget {
const AnimationDemo({Key? key}) : super(key: key);
@override
State<AnimationDemo> createState() => _AnimationDemoState();
}
class _AnimationDemoState extends State<AnimationDemo>
with SingleTickerProviderStateMixin {
late AnimationController controller;
late Animation animation;
@override
void initState() {
// TODO: implement initState
super.initState();
//1.创建 AnimationController
controller =
AnimationController(duration: Duration(milliseconds: 400), vsync: this);
//2.声明动画曲线
animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn);
//3.设置动画值的范围
animation = Tween(begin: 50.0, end: 300.0).animate(controller);
//4.监听动画
animation.addListener(() {
print(animation.value);
setState(() {});
});
//5.执行动画
//controller.forward();
}
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: () {
controller.forward();
},
child: Text('放大')),
ElevatedButton(
onPressed: () {
controller.reverse();
},
child: Text('缩小')),
ElevatedButton(
onPressed: () {
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
//反向执行动画
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
//正向
controller.forward();
}
});
controller.forward();
},
child: Text('重复')),
ElevatedButton(
onPressed: () {
controller.stop();
},
child: Text('停止')),
Icon(
Icons.accessible_forward_outlined,
color: Colors.blue,
size: animation.value,
),
Opacity(
opacity: controller.value,
child: Text('hello world'),
)
],
),
);
}
//切换至后台的时候会调用来释放资源
@override
void dispose() {
// TODO: implement dispose
super.dispose();
controller.dispose();
}
}
6.效果
二、交织动画
1.交织动画是由多个单元动画叠加而成复杂动画
2.需要给每个动画设置时间间隔(Interval)
3.Transform
- 平移-Transform.translate()
- 旋转-Transform.rotate()
- 缩放-Transform.scale()
4.代码
class StaggerAnimationDemo extends StatefulWidget {
const StaggerAnimationDemo({Key? key}) : super(key: key);
@override
State<StaggerAnimationDemo> createState() => _StaggerAnimationDemoState();
}
class _StaggerAnimationDemoState extends State<StaggerAnimationDemo>
with SingleTickerProviderStateMixin {
late AnimationController controller;
late Animation<double> animation;
late Animation sizeAnimation;
late Animation colorAnimation;
late Animation rotationAnimation;
@override
void initState() {
// TODO: implement initState
super.initState();
//1.创建 AnimationController
controller =
AnimationController(duration: Duration(seconds: 3), vsync: this);
//2.创建动画
animation = CurvedAnimation(parent: controller, curve: Interval(0.0, 0.5))
..addListener(() {});
//3.让动画反复运行
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
//反向执行动画
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
//正向
controller.forward();
}
});
//4.设置其他动画
sizeAnimation = Tween(begin: 0.0, end: 200.0).animate(animation); //需要将animation设为<double>
colorAnimation = ColorTween(begin: Colors.yellow, end: Colors.red).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.5, 0.8, curve: Curves.bounceIn)))
..addListener(() {
setState(() {});
});
rotationAnimation = Tween(begin: 0.0, end: 2 * pi).animate(CurvedAnimation(
parent: controller,
curve: Interval(0.8, 1.0, curve: Curves.easeIn),
));
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: () {
controller.forward();
},
child: Text('重复')),
ElevatedButton(
onPressed: () {
controller.stop();
},
child: Text('停止')),
Icon(
Icons.accessible_forward_outlined,
color: Colors.blue,
size: animation.value,
),
Opacity(
opacity: controller.value,
child: Transform.rotate(
angle: rotationAnimation.value,
child: Container(
width: sizeAnimation.value,
color: colorAnimation.value,
height: sizeAnimation.value,
),
))
],
),
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
controller.dispose();
}
}
5.效果
三、Hero动画
1.Hero动画是拥有实现跨页面的动画效果
- 在不同页面中,声明一个共享组件(Hero)
- 由于共享组件在不同页面中的位置、外观等不同,路由切换时,形成动画效果
2.步骤
- 在页面A中定义起始Hero组件(source hero),声明tag
- 在页面A中定义目标Hero组件(destination hero),绑定相同的tag
- 页面跳转时,通过Navigator,传递tag
3.Hero组件
- tag(路由切换时,共享组件的标记)
- child(声明子组件)
4.代码
class HeroAnimationDemo extends StatelessWidget {
const HeroAnimationDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 50),
child: GridView.extent(
maxCrossAxisExtent: 300.0,
mainAxisSpacing: 20,
children: List.generate(20, (index){
String imageURL='https://picsum.photos/id/$index/300/400';
return GestureDetector(
onTap:(){
Navigator.push(context, MaterialPageRoute(builder: (BuildContext ctx){
return ImageDetail(imageURL);
}));
},
child: Hero(
tag: imageURL,
child: Image.network(imageURL),
),
);
}),
),
);
}
}
class ImageDetail extends StatelessWidget {
final String imageURl;
ImageDetail(this.imageURl);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: GestureDetector(
onTap: (){
//调回去
Navigator.pop(context);
},
child: Hero(
tag: imageURl,
child: Image.network(
imageURl,
width: double.infinity,
fit: BoxFit.cover,
),
),
),
),
);
}
}