开启Fluter基础之旅<六>-------自定义View、牛逼动画效果
这一次继续来操练Flutter的基础,待这篇写完之后接下来则得用一个小项目来对之前所学的Flutter进行一个综合应用巩固的目的,下面开撸。
自定义View:
概述:
与Flutter自带Widget一样,自定义view也会被Skia引擎编译成原生代码,性能是一样的。它也存在两个元素:
画面Canvas:
- drawLine:画直线
- drawCircle:画圆
- drawOval:画椭圆
- drawRect:画矩形
- drawPoint:画点
- drawCircle:画圆
画笔Paint:
案例效果:
具体实现:
1、先来监听按下的坐标点,并存入到集合当中。
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '自定义画笔', theme: ThemeData.light(), home: HomePage(), ); } } class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Container( color: Colors.white, child: new Scaffold( body: CustomPaints(), )); } } class CustomPaints extends StatefulWidget { @override _CustomPaintState createState() => _CustomPaintState(); } class _CustomPaintState extends State<CustomPaints> { List<Offset> list = new List(); @override Widget build(BuildContext context) { return GestureDetector( ///按下 onPanDown: (DragDownDetails details) { RenderBox box = context.findRenderObject(); Offset offset = box.globalToLocal(details.globalPosition); print(offset.toString()); setState(() { ///定义一个数组 用来收集点击屏幕的地方 list.add(offset); }); }, ); } }
运行看一下是否监听到了坐标点了:
嗯,木有问题。
2、自定义一个心形。
这里可以用一个贝塞尔曲线来绘制一个心型,关于这块https://www.cnblogs.com/webor2006/p/7341697.html在当时Android的绘制学习中已经学习过了,所以这里就不啰嗦了,还是比较容易理解的:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '自定义画笔', theme: ThemeData.light(), home: HomePage(), ); } } class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Container( color: Colors.white, child: new Scaffold( body: CustomPaints(), )); } } class CustomPaints extends StatefulWidget { @override _CustomPaintState createState() => _CustomPaintState(); } class _CustomPaintState extends State<CustomPaints> { List<Offset> list = new List(); @override Widget build(BuildContext context) { return GestureDetector( ///按下 onPanDown: (DragDownDetails details) { RenderBox box = context.findRenderObject(); Offset offset = box.globalToLocal(details.globalPosition); print(offset.toString()); setState(() { ///定义一个数组 用来收集点击屏幕的地方 list.add(offset); }); }, child: Center( child: CustomPaint( painter: lovePainter(), ), )); } } class lovePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { Paint paint = Paint() ..color = Colors.redAccent ..strokeCap = StrokeCap.square ..isAntiAlias = true ..strokeWidth = 20 ..strokeJoin = StrokeJoin.bevel; //定义画笔 //绘制左半边心 Path path = new Path(); var width = 50; var height = 80; path.moveTo(width / 2, height / 4); path.cubicTo((width * 6) / 7, height / 9, (width * 13) / 13, (height * 2) / 5, width / 2, (height * 7) / 12); canvas.drawPath(path, paint); //绘制右半边心 Path path2 = new Path(); path2.moveTo(width / 2, height / 4); path2.cubicTo(width / 7, height / 9, width / 21, (height * 2) / 5, width / 2, (height * 7) / 12); canvas.drawPath(path2, paint); } //刷新布局的时候 告诉flutter需要重新绘制 @override bool shouldRepaint(CustomPainter oldDelegate) { return true; } }
3、根据集合的坐标来绘制心形。
这一步骤就比较简单了,如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '自定义画笔', theme: ThemeData.light(), home: HomePage(), ); } } class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Container( color: Colors.white, child: new Scaffold( body: CustomPaints(), )); } } class CustomPaints extends StatefulWidget { @override _CustomPaintState createState() => _CustomPaintState(); } class _CustomPaintState extends State<CustomPaints> { List<Offset> list = new List(); @override Widget build(BuildContext context) { return new Stack( children: <Widget>[ GestureDetector( ///手势 想要控制手势 直接包裹widget即可 ///按下 onPanDown: (DragDownDetails details) { RenderBox box = context.findRenderObject(); Offset offset = box.globalToLocal(details.globalPosition); print(offset.toString()); setState(() { ///定义一个数组 用来收集点击屏幕的地方 list.add(offset); }); }, ), getList(), ], ); } Stack getList() { return new Stack( children: _buildItem(), ); } ///构建Stack布局的子childern,点击多少下就显示多少个♥布局 List<Positioned> _buildItem() { List<Positioned> lists = new List(); if (list.length != 0) { for (int i = 0; i < list.length; i++) { Offset offset = list[i]; lists.add(Positioned( ///这里要减去画笔的宽度 坐标才相对准确 top: offset.dy - 25, left: offset.dx - 20, child: CustomPaint( painter: lovePainter(), ), )); } return lists; } else { return lists; // return new Positioned(); } } } } class lovePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { Paint paint = Paint() ..color = Colors.redAccent ..strokeCap = StrokeCap.square ..isAntiAlias = true ..strokeWidth = 20 ..strokeJoin = StrokeJoin.bevel; //定义画笔 //绘制左半边心 Path path = new Path(); var width = 50; var height = 80; path.moveTo(width / 2, height / 4); path.cubicTo((width * 6) / 7, height / 9, (width * 13) / 13, (height * 2) / 5, width / 2, (height * 7) / 12); canvas.drawPath(path, paint); //绘制右半边心 Path path2 = new Path(); path2.moveTo(width / 2, height / 4); path2.cubicTo(width / 7, height / 9, width / 21, (height * 2) / 5, width / 2, (height * 7) / 12); canvas.drawPath(path2, paint); } //刷新布局的时候 告诉flutter需要重新绘制 @override bool shouldRepaint(CustomPainter oldDelegate) { return true; } }
最终效果就如开头所示。
牛逼动画效果:
效果:
具体实现:
这块实现起来就不是那么容易了,但是如果能搞懂其实现原理对于动画的理解也会更加深刻,所以还是尽量来慢慢理解它。
1、动画实现原理分析:
其实是由八个动画来组成的,下面来拆解一下整个动作:
动画一:
动画二:
动画三:
动画四:
动画五:
动画六:
动画七:
动画八:
然后以这八个动画无限进行轮回,这样的动画效果就出来了。
2、动画定义:
接下来不管其它的,咱们先将这八个动画给定义出来,先来绘制成四个横线,比较简单就不多解释了:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new BrickWidget( marginLeft: 0.0, ), new BrickWidget( marginLeft: 0.0, ), new BrickWidget( marginLeft: 30.0, ), new BrickWidget( marginLeft: 30.0, ), ], ), ), ); } } //横线Widget class BrickWidget extends StatelessWidget { final double marginLeft; const BrickWidget({ Key key, this.marginLeft = 15.0, }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(left: marginLeft), width: 40.0, height: 10.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), color: Color(0xffff0000), ), ); } }
运行:
接下来咱们让它按照上面分析的顺序一一动起来:
动画一:
这里采用AnimatedWidget来实现,关于它的使用可以参考:https://www.cnblogs.com/webor2006/p/12649906.html,由于AnimatedWidget是一个抽象类:
所以需要定义一个具体实现类,关于具体的动画实现就不详细解释了,贴出代码也比较好理解的:
import 'dart:math'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin { Tween<double> _tween; AnimationController _animationController; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 5)) //总共动画是执行5秒 ..forward(); _tween = Tween<double>(begin: 0.0, end: 1.0); } @override void dispose() { super.dispose(); _animationController.dispose(); } Animation get animOne => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( //由于会有八个动画,这里就分成8等份从0执行到1 0.0, 0.125, curve: Curves.linear, ), ), ); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new AnimatedBrick( animation: animOne, controller: _animationController, ), new BrickWidget( marginLeft: 0.0, ), new BrickWidget( marginLeft: 30.0, ), new BrickWidget( marginLeft: 30.0, ), ], ), ), ); } } class AnimatedBrick extends AnimatedWidget { final AnimationController controller; final Animation animation; AnimatedBrick({ Key key, this.controller, this.animation, }) : super(key: key, listenable: controller); Matrix4 clockWise(animation) => Matrix4.rotationZ( animation.value * pi * 2.0 * 0.5); //其中pi*2.0*0.5则为180,每次旋转都是180度 @override Widget build(BuildContext context) { return Transform( //使用平移动画 alignment: Alignment.centerLeft, transform: clockWise(animation), child: BrickWidget( marginLeft: 0.0, ), ); } } //横线Widget class BrickWidget extends StatelessWidget { final double marginLeft; const BrickWidget({ Key key, this.marginLeft = 15.0, }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(left: marginLeft), width: 40.0, height: 10.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), color: Color(0xffff0000), ), ); } }
其中用到了CurvedAnimation,运行看一下效果:
动画二:
由于动画二跟动画一刚好都是作用在同一个Widget上面的,所以此时咱们将这俩个动画以数组的方式传进来:
import 'dart:math'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin { Tween<double> _tween; AnimationController _animationController; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 5)) //总共动画是执行5秒 ..repeat(); _tween = Tween<double>(begin: 0.0, end: 1.0); } @override void dispose() { super.dispose(); _animationController.dispose(); } Animation get animOne => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( //由于会有八个动画,这里就分成8等份从0执行到1 0.0, 0.125, curve: Curves.linear, ), ), ); Animation get animTwo => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.125, 0.25, curve: Curves.linear, ), ), ); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new AnimatedBrick( animations: [animOne, animTwo], controller: _animationController, ), new BrickWidget( marginLeft: 0.0, ), new BrickWidget( marginLeft: 30.0, ), new BrickWidget( marginLeft: 30.0, ), ], ), ), ); } } class AnimatedBrick extends AnimatedWidget { final AnimationController controller; final List<Animation> animations; AnimatedBrick({ Key key, this.controller, this.animations, }) : super(key: key, listenable: controller); Matrix4 clockWise(animation) => Matrix4.rotationZ( animation.value * pi * 2.0 * 0.5); //其中pi*2.0*0.5则为180,每次旋转都是180度 @override Widget build(BuildContext context) { return Transform( //使用平移动画 alignment: Alignment.centerLeft, transform: clockWise(animations[0]), child: Transform( //里面又套一个平移动画 alignment: Alignment.centerLeft, transform: clockWise(animations[1]), child: BrickWidget( marginLeft: 0.0, )), ); } } //横线Widget class BrickWidget extends StatelessWidget { final double marginLeft; const BrickWidget({ Key key, this.marginLeft = 15.0, }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(left: marginLeft), width: 40.0, height: 10.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), color: Color(0xffff0000), ), ); } }
运行:
动画三、动画八:
目前已经实现了二个动画了,其实接下来的实现思路基本雷同,但是此时是逆时针进行旋转了,所以需要加入一个开关来控制旋转方向了,下面直接看代码:
但是此时它只传了一个动画,其实它还有一个对应也是逆时针配套的动画的,也就是动画八:
所以咱们可以先将动画八给定义一下,保持一个模板调用传进去的动画都是2个:
此时咱们就可以将动画八传到数组当中了:
整个代码目前为:
import 'dart:math'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin { Tween<double> _tween; AnimationController _animationController; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 5)) //总共动画是执行5秒 ..repeat(); _tween = Tween<double>(begin: 0.0, end: 1.0); } @override void dispose() { super.dispose(); _animationController.dispose(); } Animation get animOne => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( //由于会有八个动画,这里就分成8等份从0执行到1 0.0, 0.125, curve: Curves.linear, ), ), ); Animation get animTwo => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.125, 0.25, curve: Curves.linear, ), ), ); Animation get animThree => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.25, 0.375, curve: Curves.linear, ), ), ); Animation get animEight => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.875, 1.0, curve: Curves.linear, ), ), ); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new AnimatedBrick( animations: [animOne, animTwo], controller: _animationController, isClockWise: true, ), new AnimatedBrick( animations: [animThree, animEight], controller: _animationController, isClockWise: false, ), new BrickWidget( marginLeft: 30.0, ), new BrickWidget( marginLeft: 30.0, ), ], ), ), ); } } class AnimatedBrick extends AnimatedWidget { final AnimationController controller; final List<Animation> animations; final bool isClockWise; //是否是顺时针旋转 AnimatedBrick({ Key key, this.controller, this.animations, this.isClockWise, }) : super(key: key, listenable: controller); Matrix4 clockWise(animation) => Matrix4.rotationZ( animation.value * pi * 2.0 * 0.5); //其中pi*2.0*0.5则为180,每次旋转都是180度,顺时针旋转 Matrix4 antiClockWise(animation) => Matrix4.rotationZ(-(animation.value * pi * 2.0 * 0.5)); //逆时针旋转 @override Widget build(BuildContext context) { var firstTransformation = isClockWise ? clockWise(animations[0]) : antiClockWise(animations[0]); var secondTransformation = isClockWise ? clockWise(animations[1]) : antiClockWise(animations[1]); return Transform( //使用平移动画 alignment: Alignment.centerLeft, transform: firstTransformation, child: Transform( //里面又套一个平移动画 alignment: Alignment.centerLeft, transform: secondTransformation, child: BrickWidget( marginLeft: 0.0, )), ); } } //横线Widget class BrickWidget extends StatelessWidget { final double marginLeft; const BrickWidget({ Key key, this.marginLeft = 15.0, }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(left: marginLeft), width: 40.0, height: 10.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), color: Color(0xffff0000), ), ); } }
此时看一下运行效果是否如预期:
呃,貌似第二根执行的有问题,问题出在旋转轴出问题了:
我们应该是按这个轴来进行旋转才行:
为啥是这样呢?因为此时我们将一个属性写死了:
那如何修整呢?此时又需要加一个参数了:
此时在实例化AnimatedWidget时就需要指定这个属性了:
再运行:
动画四、动画七:
有了上面的经验,那么对于作用在一个对象上成套的动画就可以一起写了,也就是动画四和动画七,如下:
代码如下:
import 'dart:math'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin { Tween<double> _tween; AnimationController _animationController; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 5)) //总共动画是执行5秒 ..repeat(); _tween = Tween<double>(begin: 0.0, end: 1.0); } @override void dispose() { super.dispose(); _animationController.dispose(); } Animation get animOne => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( //由于会有八个动画,这里就分成8等份从0执行到1 0.0, 0.125, curve: Curves.linear, ), ), ); Animation get animTwo => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.125, 0.25, curve: Curves.linear, ), ), ); Animation get animThree => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.25, 0.375, curve: Curves.linear, ), ), ); Animation get animFour => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.375, 0.5, curve: Curves.linear, ), ), ); Animation get animSeven => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.75, 0.875, curve: Curves.linear, ), ), ); Animation get animEight => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.875, 1.0, curve: Curves.linear, ), ), ); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new AnimatedBrick( animations: [animOne, animTwo], controller: _animationController, alignment: Alignment.centerLeft, isClockWise: true, marginLeft: 0.0, ), new AnimatedBrick( animations: [animThree, animEight], controller: _animationController, isClockWise: false, marginLeft: 0.0, ), new AnimatedBrick( animations: [animFour, animSeven], controller: _animationController, isClockWise: true, marginLeft: 30.0, ), new BrickWidget( marginLeft: 30.0, ), ], ), ), ); } } class AnimatedBrick extends AnimatedWidget { final AnimationController controller; final List<Animation> animations; final bool isClockWise; //是否是顺时针旋转 final Alignment alignment; final double marginLeft;//由于三四个横线跟一二个横线的左间距不一样,所以再增加一个参数 AnimatedBrick({ Key key, this.controller, this.animations, this.alignment = Alignment.centerRight, this.isClockWise, this.marginLeft, }) : super(key: key, listenable: controller); Matrix4 clockWise(animation) => Matrix4.rotationZ( animation.value * pi * 2.0 * 0.5); //其中pi*2.0*0.5则为180,每次旋转都是180度,顺时针旋转 Matrix4 antiClockWise(animation) => Matrix4.rotationZ(-(animation.value * pi * 2.0 * 0.5)); //逆时针旋转 @override Widget build(BuildContext context) { var firstTransformation = isClockWise ? clockWise(animations[0]) : antiClockWise(animations[0]); var secondTransformation = isClockWise ? clockWise(animations[1]) : antiClockWise(animations[1]); return Transform( //使用平移动画 alignment: alignment, transform: firstTransformation, child: Transform( //里面又套一个平移动画 alignment: alignment, transform: secondTransformation, child: BrickWidget( marginLeft: marginLeft, )), ); } } //横线Widget class BrickWidget extends StatelessWidget { final double marginLeft; const BrickWidget({ Key key, this.marginLeft = 15.0, }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(left: marginLeft), width: 40.0, height: 10.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), color: Color(0xffff0000), ), ); } }
运行:
动画五、动画六:
最后一个横线也是对应两套动画,实现已经相当的easy了,完整代码如下:
import 'dart:math'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin { Tween<double> _tween; AnimationController _animationController; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 5)) //总共动画是执行5秒 ..repeat(); _tween = Tween<double>(begin: 0.0, end: 1.0); } @override void dispose() { super.dispose(); _animationController.dispose(); } Animation get animOne => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( //由于会有八个动画,这里就分成8等份从0执行到1 0.0, 0.125, curve: Curves.linear, ), ), ); Animation get animTwo => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.125, 0.25, curve: Curves.linear, ), ), ); Animation get animThree => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.25, 0.375, curve: Curves.linear, ), ), ); Animation get animFour => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.375, 0.5, curve: Curves.linear, ), ), ); Animation get animFive => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.5, 0.625, curve: Curves.linear, ), ), ); Animation get animSix => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.625, 0.75, curve: Curves.linear, ), ), ); Animation get animSeven => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.75, 0.875, curve: Curves.linear, ), ), ); Animation get animEight => _tween.animate( CurvedAnimation( parent: _animationController, curve: Interval( 0.875, 1.0, curve: Curves.linear, ), ), ); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new AnimatedBrick( animations: [animOne, animTwo], controller: _animationController, alignment: Alignment.centerLeft, isClockWise: true, marginLeft: 0.0, ), new AnimatedBrick( animations: [animThree, animEight], controller: _animationController, isClockWise: false, marginLeft: 0.0, ), new AnimatedBrick( animations: [animFour, animSeven], controller: _animationController, isClockWise: true, marginLeft: 30.0, ), new AnimatedBrick( animations: [animFive, animSix], controller: _animationController, marginLeft: 30.0, isClockWise: false, ), ], ), ), ); } } class AnimatedBrick extends AnimatedWidget { final AnimationController controller; final List<Animation> animations; final bool isClockWise; //是否是顺时针旋转 final Alignment alignment; final double marginLeft; AnimatedBrick({ Key key, this.controller, this.animations, this.alignment = Alignment.centerRight, this.isClockWise, this.marginLeft, }) : super(key: key, listenable: controller); Matrix4 clockWise(animation) => Matrix4.rotationZ( animation.value * pi * 2.0 * 0.5); //其中pi*2.0*0.5则为180,每次旋转都是180度,顺时针旋转 Matrix4 antiClockWise(animation) => Matrix4.rotationZ(-(animation.value * pi * 2.0 * 0.5)); //逆时针旋转 @override Widget build(BuildContext context) { var firstTransformation = isClockWise ? clockWise(animations[0]) : antiClockWise(animations[0]); var secondTransformation = isClockWise ? clockWise(animations[1]) : antiClockWise(animations[1]); return Transform( //使用平移动画 alignment: alignment, transform: firstTransformation, child: Transform( //里面又套一个平移动画 alignment: alignment, transform: secondTransformation, child: BrickWidget( marginLeft: marginLeft, )), ); } } //横线Widget class BrickWidget extends StatelessWidget { final double marginLeft; const BrickWidget({ Key key, this.marginLeft = 15.0, }) : super(key: key); @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(left: marginLeft), width: 40.0, height: 10.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), color: Color(0xffff0000), ), ); } }
最终的效果就如最开始效果所演示的那样了,说实话细节还是非常之多的,也不是特别好理解,动画实现的重点就是这八个动画时间的八等分,而且将成套的动画得要组合起来,最终就达成了一个连贯的效果。另外重点是学习如何使用Flutter的自定义动画来实现我们所需要的动画效果,其中AnimationController是非常重要的。