先上效果圖
實現步驟
繪製進度條
-
先繪製繪製波浪
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(); } 複製程式碼
-
繪製層疊的波浪,跟第一步一樣的繪製方法,可以將顏色與偏移值跟第一個波浪錯開
-
繪製圓形進度內容,這一步需要注意的是在繪製進度的時候,需要對畫布進行旋轉,具體繪製內容如下
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();
}
...
}
複製程式碼
在進度改變時,我們通過一個動畫來改變進度到指定的進度,這樣就保證了,進度發生改變時,波浪不會瞬間漲上去。