Flutter 波浪圓形進度條

kevinxie發表於2019-06-12

先上效果圖

Flutter 波浪圓形進度條

實現步驟

繪製進度條

Flutter 波浪圓形進度條

  1. 先繪製繪製波浪

     void drawWave(Canvas canvas, Offset center, double radius,
         double waveOffsetPercent, Paint paint) {
       double waveOffset = -(waveOffsetPercent * radius * 2);
       //對畫布進行圓形裁剪
       canvas.save();
       Path clipPath = Path()
         ..addOval(Rect.fromCircle(center: center, radius: radius));
       canvas.clipPath(clipPath);
     
       //表示出上圖所示的point(上圖中p)以及controlPoint(上圖中c)
       double waveProgressHeightY = (1 - percent) * radius * 2;
       Offset point1 = Offset(waveOffset, waveProgressHeightY);
       Offset point2 = Offset(waveOffset + radius, waveProgressHeightY);
       Offset point3 = Offset(waveOffset + radius * 2, waveProgressHeightY);
       Offset point4 = Offset(waveOffset + radius * 3, waveProgressHeightY);
       Offset point5 = Offset(waveOffset + radius * 4, waveProgressHeightY);
       Offset point6 = Offset(point5.dx, radius * 2 + halfWaveHeight);
       Offset point7 = Offset(point1.dx, radius * 2 + halfWaveHeight);
       Offset controlPoint1 =
           Offset(waveOffset + radius * 0.5, waveProgressHeightY - halfWaveHeight);
       Offset controlPoint2 =
           Offset(waveOffset + radius * 1.5, waveProgressHeightY + halfWaveHeight);
       Offset controlPoint3 =
           Offset(waveOffset + radius * 2.5, waveProgressHeightY - halfWaveHeight);
       Offset controlPoint4 =
           Offset(waveOffset + radius * 3.5, waveProgressHeightY + halfWaveHeight);
       //完成path的連結
       Path wavePath = Path()
         ..moveTo(point1.dx, point1.dy)
         ..quadraticBezierTo(
             controlPoint1.dx, controlPoint1.dy, point2.dx, point2.dy)
         ..quadraticBezierTo(
             controlPoint2.dx, controlPoint2.dy, point3.dx, point3.dy)
         ..quadraticBezierTo(
             controlPoint3.dx, controlPoint3.dy, point4.dx, point4.dy)
         ..quadraticBezierTo(
             controlPoint4.dx, controlPoint4.dy, point5.dx, point5.dy)
         ..lineTo(point6.dx, point6.dy)
         ..lineTo(point7.dx, point7.dy)
         ..close();
       //完成繪製
       canvas.drawPath(wavePath, paint);
       canvas.restore();
     }
    複製程式碼
  2. 繪製層疊的波浪,跟第一步一樣的繪製方法,可以將顏色與偏移值跟第一個波浪錯開

  3. 繪製圓形進度內容,這一步需要注意的是在繪製進度的時候,需要對畫布進行旋轉,具體繪製內容如下

     void drawCircleProgress(
         Canvas canvas,
         Offset center,
         double radius,
         Size size) {
       //畫進度條圓框背景
       canvas.drawCircle(center, radius, circleProgressBGPaint);
       //儲存畫布狀態
       canvas.save();
       //逆時針旋轉畫布90度
       canvas.rotate(degreeToRadian(-90));
       canvas.translate(
           -(size.height + size.width) / 2, -(size.height - size.width) / 2);
       //畫進度條圓框進度
       canvas.drawArc(
           Rect.fromCircle(center: center, radius: radius),
           degreeToRadian(0),
           degreeToRadian(percent * 360),
           false,
           circleProgressPaint);
       //恢復畫布狀態
       canvas.restore();
     }
    複製程式碼

繪製工作基本就差不多完成了

讓波浪動起來

利用動畫更改波浪的偏移值,並使動畫不停的進行重複。兩個波浪偏移的速度設定成不一致的,讓波浪看起來更協調

  void initState() {
    super.initState();
    waveAnimation = AnimationController(
      vsync: this,
      duration: widget.waveAnimationDuration,
    );
    waveAnimation.addListener(waveAnimationListener);
    lightWaveAnimation = AnimationController(
      vsync: this,
      duration: widget.lightWaveAnimationDuration,
    );
	//在lightWaveAnimationListener中獲取波浪最新的偏移值,重新整理狀態
    lightWaveAnimation.addListener(lightWaveAnimationListener);
    waveAnimation.repeat();
    lightWaveAnimation.repeat();
  }
複製程式碼

完成波浪動起來的效果之後基本上就差不多了,但是會有一個很明顯的問題,那就是在設定進度之後,進度條中的波浪是瞬間漲上去,看上去非常的不協調,所以我們還需要給進度條加上一個,進度更改時的動畫效果。

讓波浪緩緩升起來降下去

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
        vsync: this, duration: widget.progressAnimatedDuration);
    controller.addStatusListener((status) {
	  //當動畫結束時重置動畫
      if (status == AnimationStatus.completed) {
        progressAnimation.removeListener(handleProgressChange);
        controller.reset();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
	//當進度發生改變時,開始動畫
    if (currentValue != widget.value && !controller.isAnimating) {
      progressAnimation =
          controller.drive(IntTween(begin: currentValue, end: widget.value));
      progressAnimation.addListener(handleProgressChange);
      controller.forward();
    }
    ...
  }
複製程式碼

在進度改變時,我們通過一個動畫來改變進度到指定的進度,這樣就保證了,進度發生改變時,波浪不會瞬間漲上去。

專案地址

相關文章