zoukankan      html  css  js  c++  java
  • 开启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),
           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),
           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),
           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),
           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),
           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),
           40.0,
          height: 10.0,
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(15.0),
            color: Color(0xffff0000),
          ),
        );
      }
    }

    最终的效果就如最开始效果所演示的那样了,说实话细节还是非常之多的,也不是特别好理解,动画实现的重点就是这八个动画时间的八等分,而且将成套的动画得要组合起来,最终就达成了一个连贯的效果。另外重点是学习如何使用Flutter的自定义动画来实现我们所需要的动画效果,其中AnimationController是非常重要的。

  • 相关阅读:
    从一个iOS毛头小子到现在的高级工程师, 我总结了一些经验,先跟大家分享一下一些好的资料
    iOS面试题---Objective_C语言特性:分类、扩展、代理、通知、KVO、KVC、属性
    200道iOS面试题面试题整理,底层、技术亮点公司需要的这里都有
    [iOS 开发] iOS 开发从菜鸟到高手?听听他们怎么说
    2020年面向iOS开发人员的知识点总结(更新中)
    OC项目转Swift指南
    来自老程序员的10条中肯建议
    面对职业瓶颈,iOS 开发人员应该如何突破?
    憨憨程序猿,不要让你的技术被简历埋没了
    总结:实现线程同步的八种方式
  • 原文地址:https://www.cnblogs.com/webor2006/p/12845831.html
Copyright © 2011-2022 走看看