Flutter第2天--Animation動畫+粒子運動

張風捷特烈發表於2018-12-17

Flutter七日遊第二天----2018-12-17----天氣晴朗

零前言:

昨天講了一下Flutter的基礎專案,介紹了一下Dart語言,簡單的用Canvas繪個圖
本來打算今天把內建控制元件看一下的,不過既然昨天把圖都畫了,今天不玩動畫豈不可惜
今天主要是把動畫理一下,順便把Android-java粒子運動復刻到Flutter-Dart裡
最後會實現一個粒子時鐘,Java版詳見:Android原生繪圖之炫酷倒數計時
所以這篇會挺好玩的,瓜子,飲料,花生米備好,開始看吧

先挑幾個圖鎮樓吧

時鐘

運動盒 粉碎球
Flutter第2天--Animation動畫+粒子運動
Flutter第2天--Animation動畫+粒子運動
星與芒 星擴動
Flutter第2天--Animation動畫+粒子運動
Flutter第2天--Animation動畫+粒子運動

一、入門級動畫:五角星的長大

五角星的長大.gif


1.照葫蘆畫瓢

按照昨天的Flutter初始專案,我們來自己寫一個
本人是喜歡分包的,Javaer的優良習慣。至少邏輯清晰,分工明確,我建立了一個pager包
主頁面內容用AnimaPage,雖然暫時還不知道StatefulWidget是什麼,反正按照套路出牌就行了
仿照初始專案的套路寫,這裡繪圖區自定義AnimaView,打算對五角星的外接圓半徑R進行動畫

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:toly/view/anima_view.dart';

class AnimaPage extends StatefulWidget {
  @override
  _AnimaPageState createState() => _AnimaPageState();
}

class _AnimaPageState extends State<AnimaPage>{
  double _R = 25;//五角星的外接圓半徑

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("張風捷特烈"),
      ),
      body: CustomPaint(
        painter: AnimaView(context, _R),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
複製程式碼

2.AnimaView的實現:

n角星的路徑第一天已經封裝好了,不會的可以去看一下

import 'dart:ui';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:toly/helper/common_path.dart';
import 'package:toly/helper/help_view.dart';

class AnimaView extends CustomPainter {
  Paint mPaint;
  BuildContext context;
  double _R;

  AnimaView(this.context, double r) {
    mPaint = new Paint();
    mPaint.color = Colors.deepOrange;
    _R = r;
  }

  @override
  void paint(Canvas canvas, Size size) {
    var winSize = MediaQuery.of(context).size;
    drawGrid(canvas, winSize);
    drawCoo(canvas, new Size(160, 320), winSize);
    canvas.translate(160, 320);
    canvas.drawPath(nStarPath(5, _R, 50), mPaint);
  }
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}
複製程式碼

3.讓資料動起來

現在萬事俱備,只欠東風把R的值吹動就行了,下面有請Animation登場
vsync需要with SingleTickerProviderStateMixi
Tween:補間動畫----這裡即:在 25.0, ~ 150.0之間在2000ms之內均勻變化 (PS:由於程式執行情況不同,並非絕對均勻,但整體上是均勻的)
..:是級聯運算,相當於再使用此物件,這裡..也就代表animation.

class AnimaPage extends StatefulWidget {
  @override
  _AnimaPageState createState() => _AnimaPageState();
}

class _AnimaPageState extends State<AnimaPage>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation<double> animation;
  double _R = 25;

  @override
  void initState() {
    super.initState();
    // 建立 AnimationController 物件
    //|----vsync時會防止螢幕外動畫(動畫的UI不在當前螢幕時)消耗不必要的資源 
    controller = AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);
    animation = Tween(begin: 25.0, end: 150.0).animate(controller)
      ..addListener(() {
        setState(() {
          _R = animation.value;
        });
      });
  }

  @override
  void dispose() {
    super.dispose();
    controller.dispose(); // 資源釋放
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("張風捷特烈"),
      ),
      body: CustomPaint(
        painter: AnimaView(context, _R),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.forward(); //執行動畫
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
複製程式碼

這樣最簡單的動畫就動起來了


4.來理一理思路

把原始碼翻一翻,感覺整個動畫體系也不是非常複雜
套路就是:Animatable用animate方法,把一個Animation包裹一下,形成一個更厲害的Animation
至於他們的n個兒子,也就是對資料的處理不同,產生的效果不同罷了,套路知道了,一切好辦

動畫.png


可以看出api並沒有想象中的那麼多,所以別怕

動畫api.png


二、入門級動畫:五角星的綻放

前面用了補間動畫Tween,而且只動了一下,下面帶來連續運動的不均勻動畫

勻速往復動 自定義曲線 bounceInOut
Flutter第2天--Animation動畫+粒子運動
Flutter第2天--Animation動畫+粒子運動
Flutter第2天--Animation動畫+粒子運動

1.往復運動
1.1:運動狀態

你可以想象成一個人在一個範圍數字跑道上跑步:

enum AnimationStatus {
  /// The animation is stopped at the beginning
  dismissed,//在正在開始時停止了
  /// The animation is running from beginning to end
  forward,//運動中
  /// The animation is running backwards, from end to beginning
  reverse,//跑到終點,再跑回來的時候
  /// The animation is stopped at the end
  completed,//跑到終點是
}
複製程式碼

1.2:狀態的監聽

addStatusListener:可以監聽當前運動狀態:只要讓它跑完了,再往回跑就行了

 animation = Tween(begin: 25.0, end: 150.0).animate(controller)
      ..addListener(() {
        setState(() {
          _R = animation.value;
        });
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          controller.reverse();
        } else if (status == AnimationStatus.dismissed) {
          controller.forward();
        }
      });
複製程式碼

2.變速跑

就像運動員變速跑一樣,感覺就像遊戲里加buff,本來是勻速的Animation
給價格變速的buff就變速起來了,加buff的類就是Animatable,它的子類有一個CurveTween

animation = Tween(begin: 25.0, end: 150.0).animate(
        CurveTween(curve: Curves.bounceInOut).animate(controller))
複製程式碼

就這麼簡單,Curves裡有幾個內建的變速器,給原來的animation裝上就行了

bounceInOut.gif


3.自定義變速曲線
3.1:追蹤一下原始碼
---->[CurveTween]---------控制元件只有一參Curve------------------------
class CurveTween extends Animatable<double> {
  /// Creates a curve tween.
  ///
  /// The [curve] argument must not be null.
  CurveTween({ @required this.curve })
    : assert(curve != null);

  /// The curve to use when transforming the value of the animation.
  Curve curve;
  
---->[Curve]--------抽象的,找兒子去--------------------
@immutable
abstract class Curve {

---->[Curve]--------四參構造的曲線,整合我意--------------------
class Cubic extends Curve {
  /// Creates a cubic curve.
  ///
  /// Rather than creating a new instance, consider using one of the common
  /// cubic curves in [Curves].
  ///
  /// The [a], [b], [c], and [d] arguments must not be null.
  const Cubic(this.a, this.b, this.c, this.d)

複製程式碼

3.2:Chrome小工具

作為一名前端業餘愛好者,Chrome裡有個小東西很有用,
曲線生成,自帶預覽,簡直無心插柳柳成蔭。(記得掘金的頭像可以轉,有translate屬性)

Cubic的獲取.png

Cubic.gif


3.3:使用:
  animation = Tween(begin: 25.0, end: 150.0).animate(
        CurveTween(curve: Cubic(0.96, 0.13, 0.1, 1.2)).animate(controller))
複製程式碼

自定義曲線.gif

Ok,基本上就這樣,你get了嗎?


三、初級動畫:太陽的誕生

紅太陽 星與陽
Flutter第2天--Animation動畫+粒子運動
Flutter第2天--Animation動畫+粒子運動
1.紅太陽:整型int 動畫

套路學會了,這些動態改變一下n角星的尖角數,看看效果

1.1:AnimaPage裡定義尖角數動畫
Animation<int> numAnima;//n角星的尖角數動畫
int _num = 5;//n角星的尖角數動畫

 controller = AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);

    numAnima = IntTween(begin: 5, end: 220).animate(controller)
      ..addListener(() {
        setState(() {
          _num = numAnima.value;//設定屬性,重新整理介面
        });
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          controller.reverse();
        } else if (status == AnimationStatus.dismissed) {
          controller.forward();
        }
      });
複製程式碼

1.2:AnimaView增加入參
int _num;
AnimaView(this.context, {double R, int num, Color color}) {
    _num = num;
}
 
//繪製時使用_num即可
 canvas.drawPath(nStarPath(_num, 100, 50), mPaint);
複製程式碼

2.星與陽:顏色動畫

ColorTween相當於新增顏色改變的buff,入參的自己加吧,和上面一樣,給畫筆填色就行了

colorAnima =
        ColorTween(begin: Colors.yellow, end: Colors.red).animate(controller)
          ..addListener(() {
            setState(() {
              _color = colorAnima.value;
            });
          })
          ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
              controller.reverse();
            } else if (status == AnimationStatus.dismissed) {
              controller.forward();
            }
          });
複製程式碼

3.其他效果

自己玩玩吧,隨便改些屬性

星與芒 星擴動
Flutter第2天--Animation動畫+粒子運動
Flutter第2天--Animation動畫+粒子運動

星與芒:固定五角星內接圓半徑,外接圓半徑變大,角數變多,顏色變紅
星擴動:五角星內接圓半徑為外接圓一半,外接圓半徑變大,角數變多,顏色變紅

好了,預熱完了,下面進入正題

四、粒子運動

無論什麼語言只有能模擬時間流就可以有粒子動畫
粒子動畫的基礎在Android原生繪圖之讓你瞭解View的運動裡講的很詳細
思想對於所有語言都是通用的,不僅限於java,有興趣的可以詳細瞭解下

1.廢話不多說,來個運動盒再說

運動盒就是小球在盒子裡不斷彈跳的動畫,就像這樣

Flutter第2天--Animation動畫+粒子運動

1.1:新建檔案:run_ball_view.dart----->RunBallView + Ball

先把小球的實體類寫一下

class Ball {
  double aX; //加速度
  double aY; //加速度Y
  double vX; //速度X
  double vY; //速度Y
  double x; //點位X
  double y; //點位Y
  Color color; //顏色
  double r;//小球半徑

  Ball({this.x, this.y, this.color, this.r, this.aX, this.aY, this.vX, this.vY});
}
複製程式碼

1.2:畫板準備好
class RunBallView extends CustomPainter {
  Paint mPaint;
  BuildContext context;
  Ball _ball;
  Rect _limit;

  RunBallView(this.context, Ball ball, Rect limit) {
    mPaint = new Paint();
    _ball = ball;
    _limit = limit;
  }

  @override
  void paint(Canvas canvas, Size size) {
    var winSize = MediaQuery.of(context).size;
    canvas.translate(160, 320);
    mPaint.color = Color.fromARGB(148, 198, 246, 248);
    canvas.drawRect(_limit, mPaint);

    canvas.save();
    drawBall(canvas, _ball);
    canvas.restore();
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
 //繪製小球
  void drawBall(Canvas canvas, Ball ball) {
    mPaint.color = ball.color;
    canvas.drawCircle(Offset(ball.x, ball.y), ball.r, mPaint);
  }
}
複製程式碼

1.3:認識一下矩形物件Rect

這是我們需要的欄位

var limit = Rect.fromLTRB(-140, -100, 140, 100);

print("width:${limit.width}");
print("height:${limit.height}");
print("left:${limit.left}");
print("top:${limit.top}");
print("right:${limit.right}");
print("bottom:${limit.bottom}");

I/flutter (28755): width:280.0
I/flutter (28755): height:200.0
I/flutter (28755): left:-140.0
I/flutter (28755): top:-100.0
I/flutter (28755): right:140.0
I/flutter (28755): bottom:100.0
複製程式碼

1.4:新建檔案:run_ball_pager.dart---->RunBallPage

這裡只需要一個時間流,用AnimationController一個人就夠了

class RunBallPage extends StatefulWidget {
  @override
  _RunBallPageState createState() => _RunBallPageState();
}

class _RunBallPageState extends State<RunBallPage>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Ball _ball;
  var _limit = Rect.fromLTRB(-140, -100, 140, 100);//矩形邊界
    
  @override
  void initState() {
    super.initState();
    //初始化小球
    _ball = Ball(x: 0, y: 0, color: Colors.blue, r: 10, aX: 0, aY: 0, vX: 0, vY: 2);

    // 建立 AnimationController 物件
    //|----vsync時會防止螢幕外動畫(動畫的UI不在當前螢幕時)消耗不必要的資源
    controller = AnimationController(
        duration: const Duration(milliseconds: 200000), vsync: this);

    controller.addListener(() {
      updateBall();//更新小球

      setState(() {});
    });

    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        controller.reverse();
      } else if (status == AnimationStatus.dismissed) {
        controller.forward();
      }
    });
  }

  @override
  void dispose() {
    super.dispose();
    controller.dispose(); // 資源釋放
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("張風捷特烈"),
      ),
      body: CustomPaint(
        painter: RunBallView(context, _ball,_limit),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.forward(); //執行動畫
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }

//更新小球
  void updateBall() {}
}
複製程式碼

小球.png

這樣靜態小球就完成了


1.5:更新新小球位置,渲染檢視:RunBallPage#updateBall

上下彈動.gif

//更新小球位置
  void updateBall() {
    //運動學公式
    _ball.x += _ball.vX;
    _ball.y += _ball.vY;
    _ball.vX += _ball.aX;
    _ball.vY += _ball.aY;
    //限定下邊界
    if (_ball.y > _limit.bottom - _ball.r) {
      _ball.y = _limit.bottom - _ball.r;
      _ball.vY = -_ball.vY;
      _ball.color=randomRGB();//碰撞後隨機色
    }
    //限定上邊界
    if (_ball.y < _limit.top + _ball.r) {
      _ball.y = _limit.top + _ball.r;
      _ball.vY = -_ball.vY;
      _ball.color=randomRGB();//碰撞後隨機色
    }
  }
複製程式碼

1.6:運動盒的實現

運動盒.gif

//初始時附加資訊
_ball = Ball(x: 0, y: 0, color: Colors.blue, r: 10, aX: 0, aY: 0.1, vX: 2, vY: -2);

//更新小球位置
  void updateBall() {
    //運動學公式
    _ball.x += _ball.vX;
    _ball.y += _ball.vY;
    _ball.vX += _ball.aX;
    _ball.vY += _ball.aY;
    //限定下邊界
    if (_ball.y > _limit.bottom - _ball.r) {
      _ball.y = _limit.bottom - _ball.r;
      _ball.vY = -_ball.vY;
      _ball.color=randomRGB();//碰撞後隨機色
    }
    //限定上邊界
    if (_ball.y < _limit.top + _ball.r) {
      _ball.y = _limit.top + _ball.r;
      _ball.vY = -_ball.vY;
      _ball.color=randomRGB();//碰撞後隨機色
    }

    //限定左邊界
    if (_ball.x < _limit.left + _ball.r) {
      _ball.x = _limit.left + _ball.r;
      _ball.vX = -_ball.vX;
      _ball.color=randomRGB();//碰撞後隨機色
    }

    //限定右邊界
    if (_ball.x > _limit.right - _ball.r) {
      _ball.x = _limit.right - _ball.r;
      _ball.vX= -_ball.vX;
      _ball.color=randomRGB();//碰撞後隨機色
    }
  }
複製程式碼

2.粒子的運動

思路就是:用List把球裝一下,碰撞的時候,建立一個方向相反,半徑減半的球,加入集合
並將當前的球半徑減半,效果挺不錯的,實現起來也不麻煩。Android-java版可見

Flutter第2天--Animation動畫+粒子運動


2.1:改動:RunBallPage

半徑小於0.3就移除,為了不讓小球無限增加,小於0.3基本上也就卡不見了
當然你也可以自定義移除的時機

var _balls = List<Ball>();//將_ball換成集合

var ball = Ball(x: 0, y: 0, color: Colors.blue, r: 40, aX: 0.05, aY: 0.1, vX: 3, vY: -3);
_balls.add(ball);//新增一個

//更新方法
for (int i = 0; i < _balls.length; i++) {
      var ball = _balls[i];
      if (ball.r < 0.3) {
        //半徑小於0.3就移除
        _balls.removeAt(i);
      }
      //運動學公式
      ball.x += ball.vX;
      ball.y += ball.vY;
      ball.vX += ball.aX;
      ball.vY += ball.aY;
      //限定下邊界
      if (ball.y > _limit.bottom) {
        var newBall = Ball.fromBall(ball);
        newBall.r = newBall.r / 2;
        newBall.vX = -newBall.vX;
        newBall.vY = -newBall.vY;
        _balls.add(newBall);
        ball.r = ball.r / 2;

        ball.y = _limit.bottom;
        ball.vY = -ball.vY;
        ball.color = randomRGB(); //碰撞後隨機色
      }
      //限定上邊界
      if (ball.y < _limit.top) {
        ball.y = _limit.top;
        ball.vY = -ball.vY;
        ball.color = randomRGB(); //碰撞後隨機色
      }

      //限定左邊界
      if (ball.x < _limit.left) {
        ball.x = _limit.left;
        ball.vX = -ball.vX;
        ball.color = randomRGB(); //碰撞後隨機色
      }

      //限定右邊界
      if (ball.x > _limit.right) {
        var newBall = Ball.fromBall(ball);
        newBall.r = newBall.r / 2;
        newBall.vX = -newBall.vX;
        newBall.vY = -newBall.vY;
        _balls.add(newBall);
        ball.r = ball.r / 2;

        ball.x = _limit.right;
        ball.vX = -ball.vX;
        ball.color = randomRGB(); //碰撞後隨機色
      }
    }
  }
  
//傳入畫布也變成小球集合
body: CustomPaint(
        painter: RunBallView(context, _balls, _limit),
),
複製程式碼

2.2:繪製小球時:RunBallView

把小球都繪製出來就行了

//複製一個小球
Ball.fromBall(Ball ball) {
  this.x = ball.x;
  this.y = ball.y;
  this.color = ball.color;
  this.r = ball.r;
  this.aX = ball.aX;
  this.aY = ball.aY;
  this.vX = ball.vX;
  this.vY = ball.vY;
}

//------paint方法中-----------
_balls.forEach((ball) {
     drawBall(canvas, ball);
   });
複製程式碼

這樣就完成了,是不是沒有想象中的那麼複雜


五、粒子時鐘

這裡就不詳細分析,這裡的Java版已經分析的很細緻了,直接上程式碼(基本上是Java的翻譯版)
這個效果新建了一個頁面來做,digit三維陣列Dart版附在文尾

1.渲染數字

繪製1994.png

/**
 * 渲染數字
 * @param num    要顯示的數字
 * @param canvas 畫布
 */
void renderDigit(int num, Canvas canvas) {
  if (num > 10) {
    return;
  }
  for (int i = 0; i < digit[num].length; i++) {
    for (int j = 0; j < digit[num][j].length; j++) {
      if (digit[num][i][j] == 1) {
        canvas.save();
        double rX = j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)個點圓心橫座標
        double rY = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)個點圓心縱座標
        canvas.translate(rX, rY);
        mPaint.color = Colors.blue;
        canvas.drawPath(mStarPath, mPaint);
        canvas.restore();
      }
    }
  }
}
複製程式碼

2.畫布中繪製1994
canvas.save();
renderDigit(1, canvas);
canvas.translate(80, 0);
renderDigit(9, canvas);
canvas.translate(80, 0);
renderDigit(9, canvas);
canvas.translate(80, 0);
renderDigit(4, canvas);
canvas.restore();
複製程式碼

3.繪製時間

繪製時間.png

3.1:時間的簡單獲取
DateTime now = new DateTime.now();
var hour = now.hour;
var second = now.second;
var minute = now.minute;

print("hour$hour");//15
print("second$second");//57
print("minute$minute");//27
複製程式碼

3.2:繪製時間
//時
renderDigit(_now.hour ~/ 10, canvas);
canvas.translate(19 * _radius, 0);
renderDigit(_now.hour % 10, canvas);
//:
canvas.translate(19 * _radius, 0);
renderDigit(10, canvas);
//分
canvas.translate(11 * _radius, 0);
renderDigit(_now.minute ~/ 10, canvas);
canvas.translate(19 * _radius, 0);
renderDigit(_now.minute % 10, canvas);
//:
canvas.translate(18 * _radius, 0);
renderDigit(10, canvas);
//秒
canvas.translate(11 * _radius, 0);
renderDigit(_now.second ~/ 10, canvas);
canvas.translate(19 * _radius, 0);
renderDigit(_now.second % 10, canvas);
複製程式碼

3.3:用Animation動起來

運動時鐘.gif


4.加小球

方法基本上是Java版改些的,這裡不分析了,可以看Java版的分析,基本上一致

時鐘

//在類的外部定義全域性的變數
var currTime = new DateTime.now();
var tagOfBall = new DateTime.now().millisecondsSinceEpoch;
var _balls = new List<Ball>();

//構造方法中重新整理小球
  ClockView(this.context) {
    mPaint = new Paint();
    mStarPath = nStarPath(5, _radius, _radius / 2);
    _now = new DateTime.now();
    addBallsChanged();
    updateBalls();
  }


  /**
   * 新增倒數計時中改動的點---變動加球
   */
  void addBallsChanged() {
    var now = new DateTime.now();
    if (currTime.second != now.second) {
      //判斷當前時間是否改變,再將點位放到集合中
      if ((currTime.hour ~/ 10) != (now.hour ~/ 10)) {
        addBalls((-17 * 5 - 11 * 2) * _radius.toInt(), currTime.hour ~/ 10);
      }
      if ((currTime.hour % 10) != (now.hour % 10)) {
        addBalls((-17 * 4 - 11 * 2) * _radius.toInt(), currTime.hour % 10);
      }
      if ((currTime.minute ~/ 10) != (now.minute ~/ 10)) {
        addBalls((-18 * 3 - 11) * _radius.toInt(), currTime.minute ~/ 10);
      }
      if ((currTime.minute % 10) != (now.minute % 10)) {
        addBalls((-18 * 2 - 11) * _radius.toInt(), currTime.minute % 10);
      }
      if ((currTime.second ~/ 10) != (now.second ~/ 10)) {
        addBalls(-18 * _radius.toInt(), currTime.second ~/ 10);
      }
      if ((currTime.second % 10) != (now.second % 10)) {
        addBalls(0, currTime.second % 10);
        currTime = now;
      }
    }
  }

  //新增小球
  addBalls(int offsetX, int num) {
    Random random = new Random();
    for (int i = 0; i < digit[num].length; i++) {
      for (int j = 0; j < digit[num][i].length; j++) {
        if (digit[num][i][j] == 1) {
          Ball ball = new Ball();
          ball.aY = 0.1;
          ball.vX = pow(-1, random.nextInt(1000)) * 6 * random.nextDouble();
          ball.vY = 4 * random.nextDouble();
          ball.x =
              offsetX + j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)個點圓心橫座標
          ball.y = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)個點圓心縱座標
          ball.color = randomRGB();
          ball.r = _radius;
          _balls.add(ball);
        }
      }
    }
  }

  /**
   * 更新所有球的位置---讓球運動
   * 並且越界移除
   */
  void updateBalls() {
    double maxX = 400; //限定x範圍大值

    for (Ball ball in _balls) {
      ball.x += ball.vX; //x=xo+v*t-----t=1
      ball.y += ball.vY;
      ball.y += ball.aY; //v=vo+a*t-----t=1

      if (ball.y >= 160) {
        //超過Y底線,反彈
        ball.y = 160;
        ball.vY = -ball.vY * 0.99;
      }

      if (ball.x > maxX) {
        //超過X最大值,反彈
        ball.x = maxX;
        ball.vX = -ball.vX * 0.99;
      }
    }

    //5秒清一次屏
    if (new DateTime.now().millisecondsSinceEpoch - tagOfBall > 5000) {
      _balls.clear();
      tagOfBall = new DateTime.now().millisecondsSinceEpoch;
    }
  }
複製程式碼

好了,今天就這樣,內容有點多,現在Dart的語法越用越熟練了


後記:捷文規範

1.本文成長記錄及勘誤表
專案原始碼 日期 備註
V0.1-github 2018-12-17 Flutter第2天--Animation動畫+粒子運動
2.更多關於我
筆名 QQ 微信 愛好
張風捷特烈 1981462002 zdl1994328 語言
我的github 我的簡書 我的掘金 個人網站
3.宣告

1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援


icon_wx_200.png


//顏色常量
import 'dart:ui';

const colors = [
  Color(0x8833B5E5),
  Color(0x880099CC),
  Color(0x889933CC),
  Color(0x8899CC00),
  Color(0x88669900),
  Color(0x88FFBB33),
  Color(0x88FF8800),
  Color(0x88FF4444),
  Color(0x88CC0000)
];

const digit = [
  [
    [0, 0, 1, 1, 1, 0, 0],
    [0, 1, 1, 0, 1, 1, 0],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [0, 1, 1, 0, 1, 1, 0],
    [0, 0, 1, 1, 1, 0, 0]
  ], //0

  [
    [0, 0, 0, 1, 1, 0, 0],
    [0, 1, 1, 1, 1, 0, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [1, 1, 1, 1, 1, 1, 1]
  ], //1
  [
    [0, 1, 1, 1, 1, 1, 0],
    [1, 1, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 1, 1, 0, 0, 0],
    [0, 1, 1, 0, 0, 0, 0],
    [1, 1, 0, 0, 0, 0, 0],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 1, 1, 1, 1, 1]
  ], //2
  [
    [1, 1, 1, 1, 1, 1, 1],
    [0, 0, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 0, 0],
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [0, 1, 1, 1, 1, 1, 0]
  ], //3

  [
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 1, 1, 1, 0],
    [0, 0, 1, 1, 1, 1, 0],
    [0, 1, 1, 0, 1, 1, 0],
    [1, 1, 0, 0, 1, 1, 0],
    [1, 1, 1, 1, 1, 1, 1],
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 1, 1, 1, 1]
  ], //4
  [
    [1, 1, 1, 1, 1, 1, 1],
    [1, 1, 0, 0, 0, 0, 0],
    [1, 1, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 0],
    [0, 0, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [0, 1, 1, 1, 1, 1, 0]
  ], //5
  [
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 1, 1, 0, 0, 0],
    [0, 1, 1, 0, 0, 0, 0],
    [1, 1, 0, 0, 0, 0, 0],
    [1, 1, 0, 1, 1, 1, 0],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [0, 1, 1, 1, 1, 1, 0]
  ], //6
  [
    [1, 1, 1, 1, 1, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 0, 1, 1, 0, 0, 0],
    [0, 0, 1, 1, 0, 0, 0],
    [0, 0, 1, 1, 0, 0, 0],
    [0, 0, 1, 1, 0, 0, 0]
  ], //7
  [
    [0, 1, 1, 1, 1, 1, 0],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [0, 1, 1, 1, 1, 1, 0],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [0, 1, 1, 1, 1, 1, 0]
  ], //8
  [
    [0, 1, 1, 1, 1, 1, 0],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [1, 1, 0, 0, 0, 1, 1],
    [0, 1, 1, 1, 0, 1, 1],
    [0, 0, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 0, 1, 1],
    [0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 1, 1, 0, 0],
    [0, 1, 1, 0, 0, 0, 0]
  ], //9
  [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 1, 1, 0],
    [0, 1, 1, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 1, 1, 0],
    [0, 1, 1, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
  ] //:
];

複製程式碼

相關文章