flutter 图解_【Flutter 专题】83 图解自定义 ACEWave 波浪 Widget (一)
? ? ? 和尚今天嘗試一下繪制波浪的效果,雖然 pub 倉庫中已經(jīng)有成熟的插件,但和尚還是準(zhǔn)備用之前學(xué)習(xí)的 Canvas 和 Animation 嘗試自定義一個(gè) ACEWave;
1. 繪制曲線
??????繪制波浪首先需要繪制曲線,采用 Canvas 繪制貝塞爾曲線;常用的是數(shù)學(xué)中通常用的 sin(x) / cos(y) 函數(shù)即可;
??????其中和尚通過 Canvas 繪制時(shí)使用了 path.quadraticBezierTo 來繪制從第一個(gè) Point 到另一個(gè) 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. 循環(huán)動畫
??????和尚使用最常用的平移動畫來讓曲線動起來,其中注意的是:
當(dāng)?shù)谝淮蝿赢嫿Y(jié)束時(shí),通過 controller.repeat() 來實(shí)現(xiàn)循環(huán)播放;
動畫需要使用 Curves.linear 線性動畫,否則在循環(huán)播放過程中銜接不順暢;
使用動畫時(shí)均需在生命周期結(jié)束時(shí) 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. 增加波浪周期
??????在執(zhí)行循環(huán)動畫之后,發(fā)現(xiàn)動畫過程中,會有一半是空白的,此時(shí)我們增加波浪的周期即可,多繪制一個(gè)屏幕的波浪即可,和尚建議前后多繪制兩個(gè)屏幕的曲線,在循環(huán)過程中更流暢;
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. 調(diào)整波浪起始位置
??????和尚嘗試的曲線是 sin(x) 方式的,起始位置都是 (0.0, 0.0),然而多條波浪時(shí)不會都從起點(diǎn)開始;于是和尚提供了一個(gè)初始位置,來錯(cuò)開各波浪展示位置;
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. 調(diào)整波浪寬度和峰值
??????和尚調(diào)整完波浪起始位置之后對于波浪的寬度和峰值也要進(jìn)行調(diào)整,保證每條波浪效果略有不同;
??????和尚預(yù)先繪制了前中后三個(gè)屏幕曲線,在測試過程中,若屏幕并非是曲線周期倍數(shù)時(shí),銜接過程中會有空余,如圖;
??????于是和尚計(jì)算波浪完整周期倍數(shù)與屏幕寬的差值作為移動點(diǎn) 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);
}
??????至此,一個(gè)基本的波浪模型基本完成,但還有很多優(yōu)化的方面,和尚在下篇中進(jìn)一步繪制波浪效果;如有錯(cuò)誤,請多多指導(dǎo)!
來源:阿策小和尚
總結(jié)
以上是生活随笔為你收集整理的flutter 图解_【Flutter 专题】83 图解自定义 ACEWave 波浪 Widget (一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于计算机书籍的收集与整理(一)
- 下一篇: 太原理工电子信焦工程_电气工程及其自动化