This is just the beginning of animation, so if you go to the end and you know how it’s done, you can go to the next one, okay

1. Animation scene preparation

First of all, how does the animation move by refreshing the screen continuously at very short intervals, and then with each refresh an element has a new displacement, just like animation

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _updateTimer();
  }


  var _now = DateTime.now();
  _updateTimer() {
    setState(() {
      _now = DateTime.now();
      _timer = Timer((Duration(milliseconds: 1)), (){
        _updateTimer();
      });
    });

  }
Copy the code


2. Draw from free fall

The more simple things are, the less likely they are to move, so we will use the simplest ball falling from the sky to play the scene, when you finish drawing, maybe you can actually find the fish ~

From now on we will treat the refresh interval as an interval (which is actually 1 microsecond), we will draw a circle on the canvas, because every time interval will refresh the canvas, so we will set the circle’s y coordinate: y = y + 0.1 (if we write 1, the ball will go too fast…).

double y = 10; class FalldownPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { y = y + 1; canvas.drawCircle(Offset(100, y), 10, Paint().. color = Colors.white.. strokeWidth = 10); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { // TODO: implement shouldRepaint return true; }}Copy the code

Well, did your ball fall, that’s not enough, you’ll see that your ball falls off the screen, and of course we want the ball to bounce up and down in the screen, and then bounce back, and we’re assuming that the velocity of the ball is equal to v, in this formula v is equal to 1,

If y is less than the height of the screen, then y is equal to y plus v and when y is higher than the height of the screen, we turn v, so v is equal to -v, when y is less than 0, we turn v, and so on

Of course, to make the later screenshots look faster, we set v = 10 and the ball bounces back to the middle of the screen

double y = 10; double v = 10; class FalldownPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { if(y > size.height/2 || y < 0) { v = -v; } y = y + v; canvas.drawCircle(Offset(100, y), 10, Paint().. color = Colors.white.. strokeWidth = 10); }}Copy the code

Ok, your ball has been shaking up and down, is it slowly interesting

But it looks like it’s shaking. Doesn’t that sound weird to you? This is just a normal change of mathematics, but we’re staunch materialist, so if the object whereabouts are such “constant motion”, that looked at also not good-looking, recall, we simulate free fall, give balls a acceleration, double baidu, this is the simplest (and possibly we can accept the upper limit of the) :

V = gt,

G here we take 10, t because it is an interval, so at the 10th t, v = 10g, that is, we can get the formula v = v + g, so:

 v = 0;

 ...
 
  @override
  void paint(Canvas canvas, Size size) {
    g = 30 / 1000;
    v = v + g;
    if(y > size.height/2 || y < 0) {
      v = -v;
    }
    y = y + v;
    canvas.drawCircle(Offset(100, y), 10, Paint()..color = Colors.white..strokeWidth = 10);
  }
Copy the code

At the same time, we change the initial value of v to 0, and let it fall naturally. We said g=10 earlier, but in the code, we changed it to G = 30/1000, because obviously the speed of g=10 is too fast

Well, I thought about it for a long time after I wrote it, g is always down, but v is the other way around, so the acceleration of the ball as it moves up is still g down, yeah, that’s all I can remember.

Take a closer look and see if it looks much better

So your ball is a perpetual motion machine?

Of course not, we use isUp to indicate if the ball is bouncing up:

 if(v > 0) {
   isUp = false;
 }
 
 if(y >= size.height/2){
   isUp = true;
   times  = times + 1;
 }
    
Copy the code

So when isUp = true, give him a bigger downward acceleration, so that he can’t get back to his original altitude, and the velocity drops to zero,

In addition, we need to make the ball bounce a little lower each time, that is, the downward acceleration will be a little more with each bounce:

Well, I found a function on the Internet, which is about the right half,

g = isUp ? Math.pow (1.2, times) * 60/1000:60/1000;Copy the code

Well, here the acceleration becomes 60/1000, isn’t that very random and g gets bigger and bigger as times gets bigger, well, it gets bigger and bigger

@override void paint(Canvas canvas, Size size) { g = isUp ? Math.pow (1.2, times) * 60/1000:60/1000; if(y > size.height/2) { v = -v; } v = v + g; y = y + v; if(v > 0) { isUp = false; } if(y >= size.height/2){ isUp = true; times = times + 1; } print(v); canvas.drawCircle(Offset(100, y), 10, Paint().. color = Colors.white.. strokeWidth = 10); }Copy the code

Eventually, because the acceleration of the ball bouncing up is increasing, the ball can’t bounce to the top and eventually the ball gets weaker and weaker and weaker….


3. It’s time for the wind to rise

So you already know how to fall, you can put a few more balls in the scene and let them fall one by one, so I don’t have to write more here

Let’s use the same logic to apply a lateral velocity to the ball, let’s not give it acceleration, just left to right, vx = vx + 0.01, and change the previous v to VY,

@override void paint(Canvas canvas, Size size) { g = isUp ? Math.pow (1.2, times) * 60/1000:60/1000; if(y > size.height/2) { vy = -vy; } if(x > size.width || x < 0) { vx = -vx; } vy = vy + g; y = y + vy; if(vy > 0) { isUp = false; } if(y >= size.height/2){ isUp = true; times = times + 1; } / / lateral movement the if (x > size. The width | | x < 0) {vx = - ag; } vx = vx + 0.01; x = x + vx; canvas.drawCircle(Offset(x, y), 10, Paint().. color = Colors.white.. strokeWidth = 10); }Copy the code


4. Let’s play more penalties

All right, at the end of the basics, let’s put a bunch of balls into the scene, and please ourselves. So let’s get serious and object oriented and make an object for the balls to store the xy distance and velocity of each ball:

class SnowBasicInfo {
  double x, y, r;
  double vx, vy, vr;
  double ax, ay, ar;
}
Copy the code

Ax, ay is the acceleration in the x and y direction, and since we only talked about the acceleration of gravity, we only have ay is equal to g,

Then we pass in an array set in Painter to draw the balls separately, well from now on it’s called snow:

List<SnowBasicInfo> snows; @override void paint(Canvas canvas, Size size) { snows.forEach((element) { element.ay = element.isUp ? Math.pow (1.2, element.uptimes) * 60/1000:60/1000; if(element.y > size.height) { element.vy = -element.vy; } element.vy = element.vy + element.ay; element.y = element.y + element.vy; if(element.vy > 0) { element.isUp = false; } if(element.y >= size.height){ element.isUp = true; element.upTimes = element.upTimes + 1; } if(element.x > size.width || element.x < 0) { element.vx = -element.vx; } element.vx = element.vx + 0.01; element.x = element.x + element.vx; canvas.drawCircle(Offset(element.x, element.y), 10, Paint().. color = Colors.white.. strokeWidth = 10); }); }Copy the code

This just turns the single ball motion above into multiple balls moving together, and then we define a randomly generated set of balls outside:

var snows = List.generate(20, (index) => SnowBasicInfo(x: Random().nextInt(400).toDouble() , y: - Random().nextInt(50).toDouble(), r: 10));
Copy the code

Here is the first random number in this article, in fact, the ball’s initial x is 400 random, y is 50 random, then we do a simple method to generate random snow:

  List<SnowBasicInfo> moreSnow() {
    return List.generate(Random().nextInt(5), (index) => SnowBasicInfo(x: Random().nextInt(400).toDouble() , y: - Random().nextInt(100).toDouble(), r: SnowInfoUtils.snowRadius));
  }
Copy the code

Okay, now for the fun, we’re going to stuff this snow list into the top painter, and at the same time, we’re going to add a random wave of snow to the original snows every time we do this, snows keep refreshing, snows keep adding: so the whole scene will look like this:

Override Widget build(BuildContext context) {// The purpose of this split time is to add new snow slowly, If (_now.millisecond % 10 == 0) {snow.addall (moreSnow()); } return Scaffold( appBar: AppBar( title: Text(_now.millisecondsSinceEpoch.toString()), ), body: Container( color: Colors.black, width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, child: CustomPaint( painter: ManyBallsPainter(snows: snow), ), ) ); }Copy the code

This will make you want to rub your hands together for the next chapter

Touch more fish?

  • Make snow fall on your screen – Prequel
  • Let snow fall on your screen – Post ii
  • Make snow fall on your screen – Extras

If you are interested in related content, welcome to touch fish duck: github.com/SpiciedCrab…