Android Flutter绘制有趣的 loading加载动画

2022-07-28 18:15:54
目录
前言效果1:圆环内滚动的球效果2:双轨运动效果3:钟摆运动总结

前言

在网络速度较慢的场景,一个有趣的加载会提高用户的耐心和对>loading 动效甚至会让用户有想弄清楚整个动效过程到底是怎么样的冲动。然而,大部分的 App的 loading 就是下面这种千篇一律的效果 —— 俗称“转圈”。

本篇我们利用Flutter 的 PathMetric来玩几个有趣的 loading 效果。

效果1:圆环内滚动的球

如上图所示,一个红色的小球在蓝色的圆环内滚动,而且在往上滚动的时候速度慢,往下滚动的时候有个明显的加速过程。这个效果实现的思路如下:

    绘制一个蓝色的圆环,在蓝色的圆环内构建一个半径更小一号的圆环路径(Path)。让红色小球在动画控制下沿着内部的圆环定义的路径运动。选择一个中间减速(上坡)两边加速的动画曲线。

    下面是实现代码:

    // 动画控制设置
    controller =
      AnimationController(duration: const Duration(seconds: 3), vsync: this);
    animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation(
      parent: controller,
      curve: Curves.slowMiddle,
    ))
    ..addListener(() {
      setState(() {});
    });
    
    // 绘制和动画控制方法
    _drawLoadingCircle(Canvas canvas, Size size) {
      var paint = Paint()..style = PaintingStyle.stroke
        ..color = Colors.blue[400]!
        ..strokeWidth = 2.0;
      var path = Path();
      final radius = 40.0;
      var center = Offset(size.width / 2, size.height / 2);
      path.addOval(Rect.fromCircle(center: center, radius: radius));
      canvas.drawPath(path, paint);
      
      var innerPath = Path();
      final ballRadius = 4.0;
      innerPath.addOval(Rect.fromCircle(center: center, radius: radius - ballRadius));
      var metrics = innerPath.computeMetrics();
      paint.color = Colors.red;
      paint.style = PaintingStyle.fill;
      for (var pathMetric in metrics) {
        var tangent = pathMetric.getTangentForOffset(pathMetric.length * animationValue);
        canvas.drawCircle(tangent!.position, ballRadius, paint);
      }
    }

    效果2:双轨运动

    上面的实现效果其实比较简单,就是绘制了一个圆和一个椭圆,然后让两个实心圆沿着路径运动。因为有了这个组合效果,趣味性增加不少,外面的椭圆看起来就像是一条卫星轨道一样。实现的逻辑如下:

      绘制一个圆和一个椭圆,二者的中心点重合;在圆和椭圆的路径上分别绘制一个小的实心圆;通过动画控制实心圆沿着大圆和椭圆的路径上运动。

      具体实现的代码如下所示。

      controller =
            AnimationController(duration: const Duration(seconds: 2), vsync: this);
        animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation(
          parent: controller,
          curve: Curves.easeInOutSine,
        ))
          ..addListener(() {
            setState(() {});
          });
      
      _drawTwinsCircle(Canvas canvas, Size size) {
        var paint = Paint()
          ..style = PaintingStyle.stroke
          ..color = Colors.blue[400]!
          ..strokeWidth = 2.0;
      
        final radius = 50.0;
        final ballRadius = 6.0;
        var center = Offset(size.width / 2, size.height / 2);
        var circlePath = Path()
          ..addOval(Rect.fromCircle(center: center, radius: radius));
        paint.style = PaintingStyle.stroke;
        paint.color = Colors.blue[400]!;
        canvas.drawPath(circlePath, paint);
      
        var circleMetrics = circlePath.computeMetrics();
        for (var pathMetric in circleMetrics) {
          var tangent = pathMetric
              .getTangentForOffset(pathMetric.length * animationValue);
      
          paint.style = PaintingStyle.fill;
          paint.color = Colors.blue;
          canvas.drawCircle(tangent!.position, ballRadius, paint);
        }
      
        paint.style = PaintingStyle.stroke;
        paint.color = Colors.green[600]!;
        var ovalPath = Path()
          ..addOval(Rect.fromCenter(center: center, width: 3 * radius, height: 40));
        canvas.drawPath(ovalPath, paint);
        var ovalMetrics = ovalPath.computeMetrics();
      
        for (var pathMetric in ovalMetrics) {
          var tangent =
              pathMetric.getTangentForOffset(pathMetric.length * animationValue);
      
          paint.style = PaintingStyle.fill;
          canvas.drawCircle(tangent!.position, ballRadius, paint);
        }
      }

      效果3:钟摆运动

      钟摆运动的示意图如下所示,一条绳子系着一个球悬挂某处,把球拉起一定的角度释放后,球就会带动绳子沿着一条圆弧来回运动,这条圆弧的半径就是绳子的长度。

      这个效果通过代码来实现的话,需要做下面的事情:

        绘制顶部的横线,代表悬挂的顶点;绘制运动的圆弧路径,以便让球沿着圆弧运动;绘制实心圆代表球,并通过动画控制沿着一条圆弧运动;用一条顶端固定,末端指向球心的直线代表绳子;当球运动到弧线的终点后,通过动画反转(reverse)控制球 返回;到起点后再正向(forward) 运动就可以实现来回运动的效果了。

        具体实现的代码如下,这里在绘制球的时候给 Paint 对象增加了一个 maskFilter 属性,以便让球看起来发光,更加好看点。

        controller =
                AnimationController(duration: const Duration(seconds: 2), vsync: this);
        animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation(
          parent: controller,
          curve: Curves.easeInOutQuart,
        ))
          ..addListener(() {
            setState(() {});
          }
          ..addStatusListener((status) {
           if (status == AnimationStatus.completed) {
             controller.reverse();
           } else if (status == AnimationStatus.dismissed) {
             controller.forward();
           }
         });
        
        _drawPendulum(Canvas canvas, Size size) {
          var paint = Paint()
            ..style = PaintingStyle.stroke
            ..color = Colors.blue[400]!
            ..strokeWidth = 2.0;
        
          final ceilWidth = 60.0;
          final pendulumHeight = 200.0;
          var ceilCenter =
              Offset(size.width / 2, size.height / 2 - pendulumHeight / 2);
          var ceilPath = Path()
            ..moveTo(ceilCenter.dx - ceilWidth / 2, ceilCenter.dy)
            ..lineTo(ceilCenter.dx + ceilWidth / 2, ceilCenter.dy);
          canvas.drawPath(ceilPath, paint);
        
          var pendulumArcPath = Path()
            ..addArc(Rect.fromCircle(center: ceilCenter, radius: pendulumHeight),
                3 * pi / 4, -pi / 2);
        
          paint.color = Colors.white70;
          var metrics = pendulumArcPath.computeMetrics();
        
          for (var pathMetric in metrics) {
            var tangent =
                pathMetric.getTangentForOffset(pathMetric.length * animationValue);
        
            canvas.drawLine(ceilCenter, tangent!.position, paint);
            paint.style = PaintingStyle.fill;
            paint.color = Colors.blue;
            paint.maskFilter = MaskFilter.blur(BlurStyle.solid, 4.0);
            canvas.drawCircle(tangent.position, 16.0, paint);
          }
        }

        总结

        本篇介绍了三种>

        以上就是Android Flutter绘制有趣的 loading加载动画的详细内容,更多关于Android Flutter加载动画的资料请关注易采站长站其它相关文章!