Flutter粒子生成演示

news/2024/7/8 2:10:12 标签: flutter, Dart

演示:

直接上代码:

Dart">import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:kq_flutter_widgets/widgets/chart/ex/extension.dart';

class ParticleView extends StatefulWidget {
  const ParticleView({super.key});

  @override
  State<StatefulWidget> createState() => ParticleViewState();
}

class ParticleViewState extends State<ParticleView>
    with TickerProviderStateMixin {
  ///动画最大值
  static double maxValue = 1000.0;
  late AnimationController controller;
  late Animation<double> animation;

  @override
  void initState() {
    super.initState();
    controller =
        AnimationController(duration: const Duration(seconds: 1), vsync: this);
    animation = Tween(begin: 0.0, end: maxValue).animate(controller)
      ..addListener(_animationListener);
    controller.repeat();
  }

  void _animationListener() {
    if (mounted) {
      setState(() {});
    }
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (v1, v2) {
      Path path = Path();
      path.moveTo(50, 50);
      path.cubicTo(50, 50, 100, 300, 300, 400);

      return CustomPaint(
        size: Size(v2.maxWidth, v2.maxHeight),
        painter: Particle(path: path),
      );
    });
  }

  @override
  void dispose() {
    controller.removeListener(_animationListener);
    controller.dispose();
    super.dispose();
  }
}

class Particle extends CustomPainter {
  ///点粒子,如果设置了点粒子,则只显示点粒子
  final Offset? point;

  ///路径粒子,优先点粒子
  final Path? path;

  ///粒子改变方式
  final ParticleChangeType type;

  ///粒子的数量
  final int startNum;
  final int endNum;

  ///粒子运行半径
  final int rr;

  ///粒子大小半径
  final int r;

  ///路径粒子密度,数值越大,密度越大
  final int pointDensity;

  Particle({
    this.path,
    this.type = ParticleChangeType.disappear,
    this.startNum = 100,
    this.endNum = 6,
    this.rr = 20,
    this.r = 1,
    this.point,
    this.pointDensity = 80,
  });

  @override
  void paint(Canvas canvas, Size size) {
    if (point != null) {
      _bezierDraw(canvas, startNum, point!);
    } else if (path != null) {
      PathMetric? pathMetric1 = path!.computeMetric();
      if (pathMetric1 != null) {
        int length1 = pathMetric1.length.toInt();
        double diff = (startNum - endNum) / length1;
        if (length1 > pointDensity) {
          int gap = length1 ~/ pointDensity;
          if (gap == 0) {
            gap = 2;
          }
          for (int i = 0; i < length1.toInt(); i = i + gap) {
            int left = (startNum - diff * i).toInt();
            Tangent? tangent1 = pathMetric1.getTangentForOffset(i.toDouble());
            if (tangent1 != null) {
              Offset cur = tangent1.position;
              _bezierDraw(canvas, left, cur);
            }
          }
        } else {
          for (int i = 0; i < length1.toInt(); i++) {
            int left = (startNum - diff * i).toInt();
            Tangent? tangent1 = pathMetric1.getTangentForOffset(i.toDouble());
            if (tangent1 != null) {
              Offset cur = tangent1.position;
              _bezierDraw(canvas, left, cur);
            }
          }
        }
      }
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }

  _bezierDraw(Canvas canvas, int left, Offset cur) {
    for (int j = 0; j < left; j++) {
      double mix = Random().nextDouble();
      int r = Random().nextInt(rr);
      double radians1 = j * 2 * pi / left;
      double x1 = r * cos(radians1) + cur.dx;
      double y1 = r * sin(radians1) + cur.dy;

      ///计算出两点间中间点往上垂直两点距地的点的坐标
      //计算坐标系中起点与终点连线与x坐标的夹角的弧度值
      double radians2 = atan2(y1 - cur.dy, x1 - cur.dx);
      //根据三角函数计算出偏移点相对于起点为原的坐标系的X的坐标
      double centerOffsetPointX = cos(Random().nextInt(2) == 1
              ? (45 * pi / 180 + radians2)
              : (45 * pi / 180 - radians2)) *
          sqrt(2) *
          r /
          2;
      //根据三角函数计算出偏移点相对于起点为原的坐标系的Y的坐标
      double centerOffsetPointY = sin(Random().nextInt(2) == 1
              ? (45 * pi / 180 + radians2)
              : (45 * pi / 180 - radians2)) *
          sqrt(2) *
          r /
          2;

      ///坐标系平移
      double moveX = centerOffsetPointX + cur.dx;
      double moveY = centerOffsetPointY + cur.dy;

      Path path2 = Path();
      path2.moveTo(cur.dx, cur.dy);
      path2.cubicTo(cur.dx, cur.dy, moveX, moveY, x1, y1);

      /*canvas.drawPath(
        path2,
        Paint()
          ..color = Colors.redAccent
          ..style = PaintingStyle.stroke,
      );*/

      ///画动画点
      PathMetric? pathMetric2 = path2.computeMetric();
      if (pathMetric2 != null) {
        double length2 = pathMetric2.length;
        Tangent? tangent2 = pathMetric2.getTangentForOffset(length2 * mix);
        if (tangent2 != null) {
          Offset cur2 = tangent2.position;
          canvas.drawCircle(
              Offset(cur2.dx, cur2.dy),
              (type == ParticleChangeType.stable
                      ? this.r
                      : type == ParticleChangeType.disappear
                          ? this.r * (1 - mix)
                          : this.r * mix)
                  .toDouble(),
              Paint()..color = Colors.redAccent..maskFilter=const MaskFilter.blur(BlurStyle.normal, 2));
        }
      }
    }
  }
}

///粒子运动的变换方式
enum ParticleChangeType {
  ///稳定,粒子运动时大小不改变
  stable,

  ///消散,由大到小消失不见
  disappear,

  ///聚合,由小到大出现后直接消失
  together,
}

 主要思路:

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


http://www.niftyadmin.cn/n/5048552.html

相关文章

支撑位和阻力位在Renko和烛台图如何使用?FPmarkets澳福3秒回答

很多投资者都知道&#xff0c;Renko图表和普通日本烛台都会采用相同的交易信号&#xff0c;即支撑位和阻力位。那么支撑位和阻力位在Renko和烛台图如何使用?FPmarkets澳福3秒回答。 这些信号在任何时间框架上都会出现&#xff0c;且在蜡烛图交易中颇受欢迎。对于Renko图表而言…

数据结构 | 队列

队列&#xff08;First In First Out&#xff09; 顺序队列 #include <iostream>class MyQueue {private:// store elementsvector<int> data; // a pointer to indicate the start positionint p_start; public:MyQueue() {p_start 0;}/** In…

Unity引擎更新收费模式:从收入分成转向游戏安装量,将会有哪些影响呢

一、前言 Unity 引擎宣布自 2024 年 1 月 1 日起&#xff0c;将根据游戏安装量对开发者进行收费。官网通知如下 收费模式如图 这张图的大致意思就是&#xff0c; 从2024年1月1日开始&#xff0c;Unity将对所有达标的用户&#xff08;开发者&#xff09;根据游戏安装量征收“安…

Serv-U FTP服务器结合cpolar内网穿透实现共享文件并且外网可远程访问——“cpolar内网穿透”

文章目录 1. 前言2. 本地FTP搭建2.1 Serv-U下载和安装2.2 Serv-U共享网页测试2.3 Cpolar下载和安装 3. 本地FTP发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 科技日益发展的今天&#xff0c;移动电子设备似乎成了我们生活的主角&#xff0c;智能…

ubuntu 14.04更新GCC版本

按最基本的apt-get install gcc-8&#xff0c;不成功&#xff0c;提示如下。 按网上说的&#xff1a;apt-get update ,apt-get upgrade 后都无效果。 apt-cache search get 搜索后&#xff0c;发现资源链接里最新的也只有4.8.4所以不行。 需要更新资源链接&#xff0c;镜像地…

电脑开机慢问题的简单处理

电脑用久了&#xff0c;开机时间要10-20分钟特别慢&#xff0c;一下介绍两种简单有效处理方式&#xff0c;这两种方式经测试不会影响原系统软件的使用&#xff1a; 方式一&#xff1a;禁用非必要启动项【效果不是很明显】 利用360里面的优化加速禁用启动项【禁用启动项还有其…

在HTTP请求中安全传输base64编码的字符串

前言 base64是一种常见的的编码格式&#xff0c;它可以把二进制数据编码成一个由大小写英文字母&#xff08;a-zA-Z&#xff09;、阿拉伯数字&#xff08;0-9&#xff09;&#xff0c;以及三个特殊字符、/、组成的字符串。 问题 但是在URL传输中&#xff0c;、/、这三个特殊…

【有关mysql的实操记录】

一. 导入导出数据 1. 导出mysql的数据库作为备份文件 mysqldump -u 用户名 -p 数据库名 >导出文件路径.sql 回车之后&#xff0c;提示输入密码. 2. 导入mysql之前备份的数据库文件 mysql -u 用户名 -p 数据库名 <导入文件路径.sql 回车之后&#xff0c;提示输入密码 …