flutter 图解_【Flutter 专题】83 图解自定义 ACEWave 波浪 Widget (一)
? ? ? 和尚今天嘗試一下繪制波浪的效果,雖然 pub 倉庫中已經有成熟的插件,但和尚還是準備用之前學習的 Canvas 和 Animation 嘗試自定義一個 ACEWave;
1. 繪制曲線
??????繪制波浪首先需要繪制曲線,采用 Canvas 繪制貝塞爾曲線;常用的是數學中通常用的 sin(x) / cos(y) 函數即可;
??????其中和尚通過 Canvas 繪制時使用了 path.quadraticBezierTo 來繪制從第一個 Point 到另一個 Point 的貝塞爾曲線;
class?_ACEWavePainter?extends?CustomPainter?{??@override
??void?paint(Canvas?canvas,?Size?size)?{
????Paint?paint?=?Paint()
??????..color?=?Colors.red..strokeCap?=?StrokeCap.round
??????..strokeWidth?=?10..style?=?PaintingStyle.stroke;
????Path?path?=?Path()
??????..moveTo(0,?500)
??????..quadraticBezierTo(size.width?/?4,?300,?size.width?/?2,?500)
??????..quadraticBezierTo(size.width?/?4?*?3,?700,?size.width,?500);
????canvas.drawPath(path,?paint);
??}
??@override
??bool?shouldRepaint(CustomPainter?oldDelegate)?=>?false;
}
2. 循環動畫
??????和尚使用最常用的平移動畫來讓曲線動起來,其中注意的是:
當第一次動畫結束時,通過 controller.repeat() 來實現循環播放;
動畫需要使用 Curves.linear 線性動畫,否則在循環播放過程中銜接不順暢;
使用動畫時均需在生命周期結束時 dispose() 銷毀動畫;
??AnimationController?_waveController;
??Animation<double>?_waveAnimation;
??int?_duration?=?2000;
??CurvedAnimation?_curvedAnimation;
??@override
??Widget?build(BuildContext?context)?{
????return?Transform.translate(
????????offset:?Offset(MediaQuery.of(context).size.width?*?_curvedAnimation.value,?0.0),
????????child:?Container(width:?MediaQuery.of(context).size.width,
????????????child:?CustomPaint(painter:?_ACEWavePainter())));
??}
??_initAnimations()?{
????_waveController?=?AnimationController(duration:?Duration(milliseconds:?_duration),?vsync:?this);
????_curvedAnimation?=?CurvedAnimation(parent:?_waveController,?curve:?Curves.linear);
????_waveAnimation?=?Tween(begin:?0.0,?end:?1.0).animate(_waveController);
????_waveAnimation.addListener(()?=>?setState(()?{}));
????_waveController.forward();
????_waveAnimation.addStatusListener((status)?{
??????switch?(status)?{
????????case?AnimationStatus.completed:
??????????_waveController.repeat();
??????????break;
????????case?AnimationStatus.dismissed:
??????????_waveController.forward();
??????????break;
????????default:
??????????break;
??????}
????});
??}
??_disposeAnimations()?{
????_waveController.dispose();
??}
??@override
??void?initState()?{
????super.initState();
????_initAnimations();
??}
??@override
??void?dispose()?{
????_disposeAnimations();
????super.dispose();
??}
}
3. 增加波浪周期
??????在執行循環動畫之后,發現動畫過程中,會有一半是空白的,此時我們增加波浪的周期即可,多繪制一個屏幕的波浪即可,和尚建議前后多繪制兩個屏幕的曲線,在循環過程中更流暢;
Path?path?=?Path()??..moveTo(0?-?size.width,?500)
??..quadraticBezierTo(size.width?/?4?-?size.width,?300,?size.width?/?2?-?size.width,?500)
??..quadraticBezierTo(size.width?/?4?*?3?-?size.width,?700,?size.width?-?size.width,?500)
??..quadraticBezierTo(size.width?/?4,?300,?size.width?/?2,?500)
??..quadraticBezierTo(size.width?/?4?*?3,?700,?size.width,?500);
canvas.drawPath(path,?paint);
4. 調整波浪起始位置
??????和尚嘗試的曲線是 sin(x) 方式的,起始位置都是 (0.0, 0.0),然而多條波浪時不會都從起點開始;于是和尚提供了一個初始位置,來錯開各波浪展示位置;
Path?path?=?Path()??..moveTo(0?-?size.width?-?startOffset,?500)
??..quadraticBezierTo(size.width?/?4?-?size.width?-?startOffset,
??????500?-?waveHeight,?size.width?/?2?-?size.width?-?startOffset,?500)
??..quadraticBezierTo(size.width?/?4?*?3?-?size.width?-?startOffset,
??????500?+?waveHeight,?size.width?-?size.width?-?startOffset,?500)
??..quadraticBezierTo(size.width?/?4?-?startOffset,?500?-?waveHeight,
??????size.width?/?2?-?startOffset,?500)
??..quadraticBezierTo(size.width?/?4?*?3?-?startOffset,?500?+?waveHeight,
??????size.width?-?startOffset,?500)
??..quadraticBezierTo(size.width?/?4?+?size.width?-?startOffset,
??????500?-?waveHeight,?size.width?/?2?+?size.width?-?startOffset,?500)
??..quadraticBezierTo(size.width?/?4?*?3?+?size.width?-?startOffset,
??????500?+?waveHeight,?size.width?+?size.width?-?startOffset,?500);
5. 調整波浪寬度和峰值
??????和尚調整完波浪起始位置之后對于波浪的寬度和峰值也要進行調整,保證每條波浪效果略有不同;
??????和尚預先繪制了前中后三個屏幕曲線,在測試過程中,若屏幕并非是曲線周期倍數時,銜接過程中會有空余,如圖;
??????于是和尚計算波浪完整周期倍數與屏幕寬的差值作為移動點 moveTo 的附加寬度即可;
for?(int?i?=?0;?i???path..moveTo(waveWidth?*?i?-?size.width?-?startOffset,?500.0)????..quadraticBezierTo(
????????_quaterWidth?+?waveWidth?*?i?-?size.width?-?startOffset,
????????500?-?waveHeight,
????????_quaterWidth?*?2?+?waveWidth?*?i?-?size.width?-?startOffset,
????????500.0)
????..moveTo(
????????_quaterWidth?*?2?+?waveWidth?*?i?-?size.width?-?startOffset,?500.0)
????..quadraticBezierTo(
????????_quaterWidth?*?3?+?waveWidth?*?i?-?size.width?-?startOffset,
????????500?+?waveHeight,
????????_quaterWidth?*?4?+?waveWidth?*?i?-?size.width?-?startOffset,
????????500.0)
????..moveTo(waveWidth?*?i?+?startOffset?+?(plusWidth),?500.0)
????..quadraticBezierTo(
????????_quaterWidth?+?waveWidth?*?i?+?startOffset?+?plusWidth,
????????500?-?waveHeight,
????????_quaterWidth?*?2?+?waveWidth?*?i?+?startOffset?+?plusWidth,
????????500.0)
????..moveTo(
????????_quaterWidth?*?2?+?waveWidth?*?i?+?startOffset?+?plusWidth,?500.0)
????..quadraticBezierTo(
????????_quaterWidth?*?3?+?waveWidth?*?i?+?startOffset?+?plusWidth,
????????500?+?waveHeight,
????????_quaterWidth?*?4?+?waveWidth?*?i?+?startOffset?+?plusWidth,
????????500.0)
????..moveTo(waveWidth?*?i?-?size.width?+?startOffset,?500.0)
????..quadraticBezierTo(
????????_quaterWidth?+?waveWidth?*?i?-?size.width?+?startOffset,
????????500?-?waveHeight,
????????_quaterWidth?*?2?+?waveWidth?*?i?-?size.width?+?startOffset,
????????500.0)
????..moveTo(
????????_quaterWidth?*?2?+?waveWidth?*?i?-?size.width?+?startOffset,?500.0)
????..quadraticBezierTo(
????????_quaterWidth?*?3?+?waveWidth?*?i?-?size.width?+?startOffset,
????????500?+?waveHeight,
????????_quaterWidth?*?4?+?waveWidth?*?i?-?size.width?+?startOffset,
????????500.0);
}
??????至此,一個基本的波浪模型基本完成,但還有很多優化的方面,和尚在下篇中進一步繪制波浪效果;如有錯誤,請多多指導!
來源:阿策小和尚
總結
以上是生活随笔為你收集整理的flutter 图解_【Flutter 专题】83 图解自定义 ACEWave 波浪 Widget (一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于计算机书籍的收集与整理(一)
- 下一篇: Python面试常用二十题总结