日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Android?Flutter繪制有趣的?loading加載動畫_Android

作者:島上碼農 ? 更新時間: 2022-09-21 編程語言

前言

在網絡速度較慢的場景,一個有趣的加載會提高用戶的耐心和對 App 的好感,有些 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);
  }
}

總結

本篇介紹了三種 Loading 動效的繪制邏輯和實現代碼,可以看到利用路徑屬性進行繪圖以及動畫控制可以實現很多有趣的動畫效果。

原文鏈接:https://juejin.cn/post/7125029565625270286

欄目分類
最近更新