一、动画代码
import 'package:flutter/material.dart';
//提供给父组件使用的key
GlobalKey<_Capriole> poltWaitKey = GlobalKey();
/* 原地跳跃动画 */
class Capriole extends StatefulWidget {
final double width;
final double height;
final Widget fillWidget; // 填充组件
final bool autoPlay; // 是否自动播放
final int animationPeriod; // 动画播放周期(影响动画速度)
final bool showShadow; // 是否显示阴影
final double shadowSize; // 阴影初始大小
Capriole(
{Key key,
this.width = 80,
this.height = 160,
this.fillWidget,
this.autoPlay = true,
this.animationPeriod = 2000,
this.showShadow = true,
this.shadowSize = 0.7})
: super(key: key);
@override
_Capriole createState() => _Capriole();
}
class _Capriole extends State<Capriole> with TickerProviderStateMixin {
AnimationController _animationController;
Animation<double> _animation;
@override
void initState() {
super.initState();
/// AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字,总时长
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: widget.animationPeriod),
);
Animation curveAnimation =
new CurvedAnimation(parent: _animationController, curve: Curves.ease);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(curveAnimation);
_animationController.addListener(() {
setState(() {});
});
if (widget.autoPlay) {
this.repeatAnimation();
}
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
double radius = widget.width / 2; //运动物体半径
double ovalWidth = widget.width * 0.8; //阴影(椭圆)宽度
double ovalHeight = ovalWidth / 2; //阴影(椭圆)高度
double moveDistance =
(widget.height - radius * 2) * 2 - ovalHeight; //运动物体移动距离
double progress = _animation.value;
double dropLen = moveDistance * progress;
double riseLen = moveDistance * (1 - progress);
return Container(
width: widget.width,
height: widget.height,
child: CustomPaint(
painter: widget.showShadow
? _ShowdowPainter(progress, widget.shadowSize)
: null,
child: Column(
children: <Widget>[
Transform.translate(
offset: Offset(0.0, progress < 0.5 ? dropLen : riseLen),
child: Container(
alignment: Alignment.center,
width: widget.width,
height: widget.width,
child: widget.fillWidget ??
ClipOval(
child: Container(
alignment: Alignment.center,
color: Colors.green,
width: 100,
height: 100,
child: Text(
'Fill',
style: TextStyle(fontSize: 20, color: Colors.white),
),
)),
),
),
],
),
),
);
}
/// 开始动画(一次)
startAnimation() {
_animationController.reset();
_animationController.forward();
}
/// 循环动画
repeatAnimation() {
_animationController.reset();
_animationController.repeat();
}
}
class _ShowdowPainter extends CustomPainter {
Paint viewPaint;
Path path;
final double progress;
final double shadowSize;
_ShowdowPainter(this.progress, this.shadowSize) {
viewPaint = Paint()
..strokeCap = StrokeCap.round
..isAntiAlias = true
..color = Colors.black
..style = PaintingStyle.fill
..strokeWidth = 5.0;
path = Path();
}
@override
void paint(Canvas canvas, Size size) {
Color paint2Color = Colors.white;
double width = size.width; //运动物体宽度
double height = size.height; //运动物体高度
double ovalWidth = width * shadowSize; //阴影(椭圆)宽度
double ovalHeight = ovalWidth / 2; //阴影(椭圆)高度
double topDistance = height - ovalHeight; //阴影(椭圆)与顶部距离
if (progress < 0.5) {
paint2Color = Color.lerp(Colors.black12, Colors.black45, progress);
} else {
paint2Color = Color.lerp(Colors.black45, Colors.black12, progress);
}
Paint paint2 = Paint()
..isAntiAlias = true
..color = paint2Color
..strokeCap = StrokeCap.round
..strokeWidth = 10
..style = PaintingStyle.fill;
if (progress < 0.5) {
canvas.drawOval(
new Rect.fromLTWH(
(width - (ovalWidth - (ovalWidth * progress))) / 2,
topDistance + (ovalHeight * progress / 2),
ovalWidth - (ovalWidth * progress),
ovalHeight - (ovalHeight * progress)),
paint2);
} else {
canvas.drawOval(
new Rect.fromLTWH(
(width - (ovalWidth * progress)) / 2,
topDistance + (ovalHeight - (ovalHeight * progress)) / 2,
ovalWidth * progress,
ovalHeight * progress),
paint2);
}
}
/// 如果绘制依赖外部状态,那么我们就应该在shouldRepaint中判断依赖的状态是否改变,
/// 如果已改变则应返回true来重绘,反之则应返回false不需要重绘。
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
二、使用
import 'package:flutter/material.dart';
import '../../compontents/animation/capriole.dart';
class Demo extends StatefulWidget {
Demo({Key key}) : super(key: key);
@override
State<Demo> createState() => _DemoState();
}
class _DemoState extends State<Demo> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(40),
child: Wrap(spacing: 20, children: <Widget>[
Capriole(),
Capriole(
fillWidget: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
color: Colors.green,
width: 60,
height: 60,
child: Icon(
Icons.android,
size: 30,
color: Colors.white,
),
),
)),
Capriole(
animationPeriod: 1000,
showShadow: false,
fillWidget: Image.network(
'https://img-home.csdnimg.cn/images/20201124032511.png')),
Capriole(
width: 200,
height: 400,
fillWidget: ClipOval(
child: Container(
alignment: Alignment.center,
color: Colors.orange,
width: 200,
height: 200,
child: Text(
'Huge',
style: TextStyle(fontSize: 20, color: Colors.white),
),
))),
]),
));
}
}
三、动画演示