flutter 简单实现浏览器H5粒子动画

flutter 简单实现浏览器H5粒子动画

我们常见H5炫酷的粒子动画,H5有的,flutter都想拥有。

1.老规矩先上图!

粒子动画2.gif

2.大致思路分析

1.先考虑画点

随机会在屏幕上产生相应的点,点会自己移动 ,x,y轴移动速度也是随机的

当点在碰到边缘时,速度变方向

2.画线

一个点和周围一定距离内的点相连,距离越远颜色透明度越低

应该要有最大连接数

3.鼠标移动

鼠标落下的点与周围一定距离的点相连

3.具体实现类

1.鼠标监听事件:MouseRegion

2.重绘控件 :CustomPainter

3.自定义view相关属性

 LiziConfig({
    Key key,
    @required this.context,
    this.vx = 4,//点x轴速度,正为右,负为左
    this.vy = 4,//点球y轴速度
    this.radius = 2,//点半径
    this.count = 100,//点个数
    this.color = const Color.fromRGBO(121, 162, 185, 1.0),//点颜色
    this.stroke = const Color.fromRGBO(130, 255, 255, 1.0),//线条颜色
    this.dist = 100,//点吸附距离
    this.eDist = 130,//鼠标吸附距离
    this.maxConn = 10,//点到点最大连接数
  }) : super(key: key);
复制代码

4.点相关属性

class Point{
  double x;//x轴坐标
  double y;//y轴坐标
  double vx;//x轴移动速度
  double vy;//y轴移动速度
  int conNum;//点连接数量
  Point(this.x, this.y, this.vx, this.vy,this.conNum);
}
复制代码

4.详细代码

1.先画出不同的点

2021-06-02_144935.png

///初始化点的坐标
for (int i = 0; i < count; i++) {
      Point point=new Point(
          Random().nextDouble()*MediaQuery.of(contextO).size.width,
          Random().nextDouble()*MediaQuery.of(contextO).size.height,
          vx / 2 - Random().nextDouble() * vx,
          vy / 2 - Random().nextDouble() * vy,0);
      points.add(point);
    }

  @override
  void paint(Canvas canvas, Size sizes) {
    ///画点
    for (int i = 0; i < points.length; i++) {
      canvas.drawCircle(Offset(points[i].x, points[i].y),
          radius.toDouble(),_paintPoint);
    }
  }
复制代码

2.移动点

///定时器去更改点位置
const oneSec = const Duration(milliseconds: 40); //间隔1秒
    qrtimer = new Timer.periodic(oneSec, (timer) {
      _drawPoint();
    });

///移动点
  void _drawPoint() {
      setState(() {
        if(points.isNotEmpty) {
          for (int i = 0; i < count; i++) {
            _borderPoint(points[i]);
            points[i].conNum=0;
          }
        }
      });
  }

  ///边界处理
  void _borderPoint(Point p) {
    Size size=MediaQuery.of(context).size;
    if(p.x<=0||p.x>=size.width){
      p.vx=-p.vx;
      p.x+=p.vx;
    }else if(p.y<=0||p.y>=size.height){
      p.vy = -p.vy;
      p.y += p.vy;
    }else{
      p.x=p.x+p.vx;
      p.y=p.y+p.vy;
    }
  }
复制代码

3.画线

 for (int i = 0; i < points.length; i++) {
      for (int j = 0; j < points.length; j++) {
        if(i!=j){///如果不是同一个点
          ///算出两点间的距离
          double dx=points[i].x-points[j].x;
          double dy=points[i].y-points[j].y;
          double distp=sqrt(dx*dx+dy*dy);
          // print("距离:"+distp.toString());

          /// 两点距离小于吸附距离,而且小于最大连接数,则画线
          if(distp <= dist && points[i].conNum <maxConn){
              points[i].conNum++;
              _paintLine.strokeWidth=0.5-distp/dist;
              _paintLine.color=Color.fromRGBO(stroke.red, stroke.green, stroke.blue, 1-distp/dist);

              canvas.drawLine(Offset(points[i].x, points[i].y), Offset(points[j].x, points[j].y), _paintLine);
          }

          ///鼠标事件
          if(mouseY>0&&mouseX>0){
            double dx=points[i].x-mouseX;
            double dy=points[i].y-mouseY;
            double distp=sqrt(dx*dx+dy*dy);
            /// 遇到鼠标吸附距离时加速,直接改变point的x,y值达到加速效果
            if(distp > dist && distp <= eDist){
              points[i].x = points[i].x + (mouseX - points[i].x) / 20;
              points[i].y = points[i].y + (mouseY - points[i].y) / 20;
            }
            if(distp <= eDist){
              _paintMouseLine.color=Color.fromRGBO(stroke.red, stroke.green, stroke.blue, 1-distp/eDist);
              canvas.drawLine(Offset(points[i].x, points[i].y), Offset(mouseX, mouseY), _paintMouseLine);
            }
          }
        }
      }
    }
复制代码

4.鼠标事件

@override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      child:  CustomPaint(
        child: MouseRegion(
          onEnter: (event){
            //print("进入x:${event.position.dx} y:${event.position.dy}");
            _mouseEvent(event.position.dx,event.position.dy);
          },
          onExit: (event){
            //print("onExit:${event.position.dx} y:${event.position.dy}");
            _mouseEvent(-1,-1);
          },
          onHover: (event){
            // print("移动x:${event.position.dx} y:${event.position.dy}");
            _mouseEvent(event.position.dx,event.position.dy);
          },
        ),
        painter: PaintLizi(
            this.radius, this.count, this.color, this.stroke,this.maxConn,this.dist,points,this.mouseX,this.mouseY,this.eDist),
      ),
    );
  }

  ///鼠标事件
  void _mouseEvent(double x,double y){
    setState(() {
      mouseX=x;
      mouseY=y;
      Size size=MediaQuery.of(context).size;
      if(mouseX>=size.width-10||mouseX<=10){
        mouseX=-1;
      }
      if(mouseY>=size.height-10||mouseY<=10){
        mouseY=-1;
      }
    });
  }
复制代码

5.遇到的问题

问题1:MouseRegion包裹CustomPaint,监听事件失效,如下

///监听事件失效
return MouseRegion(
      onEnter: (event){
        
      },
      child: CustomPaint(
        painter: PaintLizi(
            this.radius, this.count, this.color, this.stroke,this.maxConn,this.dist,points,this.mouseX,this.mouseY,this.eDist),
          )
    );


///正确方法 MouseRegion作为CustomPaint的child
CustomPaint(
        child: MouseRegion(
          onEnter: (event){
            //print("进入x:${event.position.dx} y:${event.position.dy}");
            _mouseEvent(event.position.dx,event.position.dy);
          },
          onExit: (event){
            //print("onExit:${event.position.dx} y:${event.position.dy}");
            _mouseEvent(-1,-1);
          },
          onHover: (event){
            // print("移动x:${event.position.dx} y:${event.position.dy}");
            _mouseEvent(event.position.dx,event.position.dy);
          },
        ),
        painter: PaintLizi(
            this.radius, this.count, this.color, this.stroke,this.maxConn,this.dist,points,this.mouseX,this.mouseY,this.eDist),
      ),
复制代码

问题2:onExit: (event){}

鼠标移除事件在鼠标移到浏览器外并没有回调。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享