前言
本质上来说 flutter并没有属性动画这个玩意,但是这个思路和android的属性动画差不多,就这样写了
目标功能
实现一个动画 5-4-3-2-1 倒计时 同时旋转加缩放,旋转先顺时针再反向
主要用到的api
- AnimationController 动画控制器,就是控制线性产生 0-1之间的小数
- CurvedAnimation 插值计算器,这个和android原生的插值计算器类似,作用就是传入 一个 0-1之间的小数,返回一个经过插值处理的值
- Tween 给定 begin 和 end ,传入小数,返回begin 和end 之间的一个值
- AnimatedBuilder 绑定 AnimationController,AnimationController变化时会刷新
- TickerProviderStateMixin state绑定的view 在屏幕外时依然动画
比较有意思的代码
这里自定义了一个 插值计算器 代码如下
class ReversCurve extends Curve {
const ReversCurve(this.threshold) : assert(threshold != null);
final double threshold;
@override
double transformInternal(double t) {
assert(threshold > 0.0);
assert(threshold < 1.0);
if (t<=threshold){
return (t/threshold);
}else {
return (1-t)/(1-threshold);
}
}
}
实现的是先正向后反向
整体代码
import 'package:flutter/material.dart';
import 'dart:math';
class AnimationView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return AnimationViewState();
}
}
class ReversCurve extends Curve {
const ReversCurve(this.threshold) : assert(threshold != null);
final double threshold;
@override
double transformInternal(double t) {
assert(threshold > 0.0);
assert(threshold < 1.0);
if (t<=threshold){
return (t/threshold);
}else {
return (1-t)/(1-threshold);
}
}
}
class AnimationViewState extends State<AnimationView>
with TickerProviderStateMixin {
AnimationController controller;
var scaleAnimation = Tween(begin: 1.0, end: 2.0);
var rotationAnimation = Tween(begin: 0.0, end: 2 * pi);
IntTween countDown=IntTween(begin:5,end:1);
double screenHeight = 0;
CurvedAnimation cur;
AnimationViewState() : super() {
controller =
AnimationController(vsync: this, duration: Duration(seconds: 2));
cur= CurvedAnimation(parent: controller, curve:ReversCurve(0.6) );
}
@override
void initState() {
// TODO: implement initState
super.initState();
}
void _startAnimation() async {
try {
controller.reset();
} catch (e) {
print(e);
}
try {
await controller.forward().orCancel;
} catch (e) {
print(e);
}
}
@override
void dispose() {
super.dispose();
controller?.dispose();
}
@override
Widget build(BuildContext context) {
if (screenHeight == 0.0) {
screenHeight = MediaQuery.of(context).size.height * 0.4;
}
return Column(
children: <Widget>[
Container(
height: screenHeight,
child: Center(
child: AnimatedBuilder(
animation: controller,
builder: (context, widgt) {
return Transform(
transform: Matrix4.rotationZ(
rotationAnimation.evaluate(cur)),
alignment: Alignment.center,
child: Transform.scale(
scale: scaleAnimation.evaluate(controller),
child: Text("${countDown.evaluate(controller)}"),
),
);
}),
),
),
RaisedButton(
child: Text("rotate"),
onPressed: _startAnimation,
)
],
);
}
}