flutter 簡單實現瀏覽器H5粒子動畫
我們常見H5炫酷的粒子動畫,H5有的,flutter都想擁有。
1.老規矩先上圖!
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.先畫出不同的點
///初始化點的座標
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){}
滑鼠移除事件在滑鼠移到瀏覽器外並沒有回撥。