携手创作,共同成长!这是我参与「日新方案 8 月更文挑战」的第1天,点击检查活动概况

前言

在网络速度较慢的场景,一个风趣的加载会提高用户的耐性和对 App 的好感,有些 loading 动效甚至会让用户有想弄清楚整个动效进程到底是怎么样的冲动。但是,大部分的 App的 loading 便是下面这种千篇一律的作用 —— 俗称“转圈”。

普通的加载千篇一律,有趣的 loading 万里挑一

普通的加载千篇一律,有趣的 loading 万里挑一

本篇咱们使用FlutterPathMetric来玩几个风趣的 loading 作用。

作用1:圆环内翻滚的球

普通的加载千篇一律,有趣的 loading 万里挑一

如上图所示,一个红色的小球在蓝色的圆环内翻滚,而且在往上翻滚的时分速度慢,往下翻滚的时分有个显着的加快进程。这个作用完成的思路如下:

  • 制作一个蓝色的圆环,在蓝色的圆环内构建一个半径更小一号的圆环途径(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:双轨运动

普通的加载千篇一律,有趣的 loading 万里挑一

上面的完成作用其实比较简单,便是制作了一个圆和一个椭圆,然后让两个实心圆沿着途径运动。因为有了这个组合作用,趣味性增加不少,外面的椭圆看起来就像是一条卫星轨迹相同。完成的逻辑如下:

  • 制作一个圆和一个椭圆,二者的中心点重合;
  • 在圆和椭圆的途径上别离制作一个小的实心圆;
  • 经过动画操控实心圆沿着大圆和椭圆的途径上运动。

详细完成的代码如下所示。

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:钟摆运动

普通的加载千篇一律,有趣的 loading 万里挑一
钟摆运动的示意图如下所示,一条绳子系着一个球悬挂某处,把球拉起一定的视点开释后,球就会带动绳子沿着一条圆弧来回运动,这条圆弧的半径便是绳子的长度。
普通的加载千篇一律,有趣的 loading 万里挑一
这个作用经过代码来完成的话,需要做下面的事情:

  • 制作顶部的横线,代表悬挂的极点;
  • 制作运动的圆弧途径,以便让球沿着圆弧运动;
  • 制作实心圆代表球,并经过动画操控沿着一条圆弧运动;
  • 用一条顶端固定,结尾指向球心的直线代表绳子;
  • 当球运动到弧线的结尾后,经过动画回转(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);
  }
}

总结

本篇介绍了三种 Loading 动效的制作逻辑和完成代码,能够看到使用途径属性进行绘图以及动画操控能够完成许多风趣的动画作用。

我是岛上码农,微信公众号同名,这是Flutter 入门与实战的专栏文章,供给体系化的 Flutter 学习文章。对应源码请看这儿:Flutter 入门与实战专栏源码。如有问题能够加本人微信沟通,微信号:island-coder

:觉得有收成请点个赞鼓舞一下!

:保藏文章,方便回看哦!

:谈论沟通,相互前进!