• Flutter粒子生成演示


    演示:

    直接上代码:

    1. import 'dart:math';
    2. import 'dart:ui';
    3. import 'package:flutter/material.dart';
    4. import 'package:kq_flutter_widgets/widgets/chart/ex/extension.dart';
    5. class ParticleView extends StatefulWidget {
    6. const ParticleView({super.key});
    7. @override
    8. State createState() => ParticleViewState();
    9. }
    10. class ParticleViewState extends State<ParticleView>
    11. with TickerProviderStateMixin {
    12. ///动画最大值
    13. static double maxValue = 1000.0;
    14. late AnimationController controller;
    15. late Animation<double> animation;
    16. @override
    17. void initState() {
    18. super.initState();
    19. controller =
    20. AnimationController(duration: const Duration(seconds: 1), vsync: this);
    21. animation = Tween(begin: 0.0, end: maxValue).animate(controller)
    22. ..addListener(_animationListener);
    23. controller.repeat();
    24. }
    25. void _animationListener() {
    26. if (mounted) {
    27. setState(() {});
    28. }
    29. }
    30. @override
    31. Widget build(BuildContext context) {
    32. return LayoutBuilder(builder: (v1, v2) {
    33. Path path = Path();
    34. path.moveTo(50, 50);
    35. path.cubicTo(50, 50, 100, 300, 300, 400);
    36. return CustomPaint(
    37. size: Size(v2.maxWidth, v2.maxHeight),
    38. painter: Particle(path: path),
    39. );
    40. });
    41. }
    42. @override
    43. void dispose() {
    44. controller.removeListener(_animationListener);
    45. controller.dispose();
    46. super.dispose();
    47. }
    48. }
    49. class Particle extends CustomPainter {
    50. ///点粒子,如果设置了点粒子,则只显示点粒子
    51. final Offset? point;
    52. ///路径粒子,优先点粒子
    53. final Path? path;
    54. ///粒子改变方式
    55. final ParticleChangeType type;
    56. ///粒子的数量
    57. final int startNum;
    58. final int endNum;
    59. ///粒子运行半径
    60. final int rr;
    61. ///粒子大小半径
    62. final int r;
    63. ///路径粒子密度,数值越大,密度越大
    64. final int pointDensity;
    65. Particle({
    66. this.path,
    67. this.type = ParticleChangeType.disappear,
    68. this.startNum = 100,
    69. this.endNum = 6,
    70. this.rr = 20,
    71. this.r = 1,
    72. this.point,
    73. this.pointDensity = 80,
    74. });
    75. @override
    76. void paint(Canvas canvas, Size size) {
    77. if (point != null) {
    78. _bezierDraw(canvas, startNum, point!);
    79. } else if (path != null) {
    80. PathMetric? pathMetric1 = path!.computeMetric();
    81. if (pathMetric1 != null) {
    82. int length1 = pathMetric1.length.toInt();
    83. double diff = (startNum - endNum) / length1;
    84. if (length1 > pointDensity) {
    85. int gap = length1 ~/ pointDensity;
    86. if (gap == 0) {
    87. gap = 2;
    88. }
    89. for (int i = 0; i < length1.toInt(); i = i + gap) {
    90. int left = (startNum - diff * i).toInt();
    91. Tangent? tangent1 = pathMetric1.getTangentForOffset(i.toDouble());
    92. if (tangent1 != null) {
    93. Offset cur = tangent1.position;
    94. _bezierDraw(canvas, left, cur);
    95. }
    96. }
    97. } else {
    98. for (int i = 0; i < length1.toInt(); i++) {
    99. int left = (startNum - diff * i).toInt();
    100. Tangent? tangent1 = pathMetric1.getTangentForOffset(i.toDouble());
    101. if (tangent1 != null) {
    102. Offset cur = tangent1.position;
    103. _bezierDraw(canvas, left, cur);
    104. }
    105. }
    106. }
    107. }
    108. }
    109. }
    110. @override
    111. bool shouldRepaint(covariant CustomPainter oldDelegate) {
    112. return true;
    113. }
    114. _bezierDraw(Canvas canvas, int left, Offset cur) {
    115. for (int j = 0; j < left; j++) {
    116. double mix = Random().nextDouble();
    117. int r = Random().nextInt(rr);
    118. double radians1 = j * 2 * pi / left;
    119. double x1 = r * cos(radians1) + cur.dx;
    120. double y1 = r * sin(radians1) + cur.dy;
    121. ///计算出两点间中间点往上垂直两点距地的点的坐标
    122. //计算坐标系中起点与终点连线与x坐标的夹角的弧度值
    123. double radians2 = atan2(y1 - cur.dy, x1 - cur.dx);
    124. //根据三角函数计算出偏移点相对于起点为原的坐标系的X的坐标
    125. double centerOffsetPointX = cos(Random().nextInt(2) == 1
    126. ? (45 * pi / 180 + radians2)
    127. : (45 * pi / 180 - radians2)) *
    128. sqrt(2) *
    129. r /
    130. 2;
    131. //根据三角函数计算出偏移点相对于起点为原的坐标系的Y的坐标
    132. double centerOffsetPointY = sin(Random().nextInt(2) == 1
    133. ? (45 * pi / 180 + radians2)
    134. : (45 * pi / 180 - radians2)) *
    135. sqrt(2) *
    136. r /
    137. 2;
    138. ///坐标系平移
    139. double moveX = centerOffsetPointX + cur.dx;
    140. double moveY = centerOffsetPointY + cur.dy;
    141. Path path2 = Path();
    142. path2.moveTo(cur.dx, cur.dy);
    143. path2.cubicTo(cur.dx, cur.dy, moveX, moveY, x1, y1);
    144. /*canvas.drawPath(
    145. path2,
    146. Paint()
    147. ..color = Colors.redAccent
    148. ..style = PaintingStyle.stroke,
    149. );*/
    150. ///画动画点
    151. PathMetric? pathMetric2 = path2.computeMetric();
    152. if (pathMetric2 != null) {
    153. double length2 = pathMetric2.length;
    154. Tangent? tangent2 = pathMetric2.getTangentForOffset(length2 * mix);
    155. if (tangent2 != null) {
    156. Offset cur2 = tangent2.position;
    157. canvas.drawCircle(
    158. Offset(cur2.dx, cur2.dy),
    159. (type == ParticleChangeType.stable
    160. ? this.r
    161. : type == ParticleChangeType.disappear
    162. ? this.r * (1 - mix)
    163. : this.r * mix)
    164. .toDouble(),
    165. Paint()..color = Colors.redAccent..maskFilter=const MaskFilter.blur(BlurStyle.normal, 2));
    166. }
    167. }
    168. }
    169. }
    170. }
    171. ///粒子运动的变换方式
    172. enum ParticleChangeType {
    173. ///稳定,粒子运动时大小不改变
    174. stable,
    175. ///消散,由大到小消失不见
    176. disappear,
    177. ///聚合,由小到大出现后直接消失
    178. together,
    179. }

     主要思路:

    利用flutter的画布绘图,随机根据Path生成一些点,然后绘制路径,然后绘制路径上每一个点往四周动画运动的小球,小球在运动到一定的距离后,会消失,周而复始,达到粒子生成与泯灭的效果。

  • 相关阅读:
    3种工厂模式
    自定义类型:结构体,枚举,联合
    架构设计 - MySQL 插入数据性能优化策略
    毕业设计源码基于JAVA的课程设计管理系统的设计与实现
    【IEEE独立出版、有确定的ISBN号】第三届能源与电力系统国际学术会议 (ICEEPS 2024)
    linux 批量修改密码(二)
    BMP编程实践1:C语言实现bmp位图分析与创建
    rxjava 工作原理分析 调用链分析
    Spring Security 前后端分离(前端篇)
    [开源]基于Vue的拖拽式数据报表设计器,为简化开发提高效率而生
  • 原文地址:https://blog.csdn.net/u012800952/article/details/133136711