Marcos Placona
Marcos Placona
Marcos an experienced Developer Relations leader turned solopreneur, GDE and in ❤️ with Open Source
5 min read

Categories

  • Dart

After my last example showing you how to create a matrix effect with Dart, I thought it would be cool, to follow up with another example that used the same concepts, but went into slightly more details.

It’s also a bit more complex than the last one, as on this one, we use a few more canvas properties, such as arc to generate our flurries, and beginPath to reset my objects so they’re actual flakes, and not a long rain trace (more on this later).

As per the previous example, I will be using canvas to display my animation.

The final product should look like the following:

Dart Snow

Although the animation will be way better than the gif image above.

I start by creating a class that will hold each of my flakes. Not much to explain here. This is only a PODO (Patent Pending) 😉

class Flake{
  double _x, _y, _r, _d;
  Flake(double x, double y, double r, double d){
    this._x = x;
    this._y = y;
    this._r = r;
    this._d = d;
  }
}

I then go ahead and initialize some of the variables and constants that will be used by my application. I do it in such way I can experiment with values and get my application to behave in different ways depending on what values I choose.

// Holds all my flakes
var flakes = [];
 
// Maximum number of flakes at a time, and speed in which they fall
const int maxFlakes= 100; //max number of flakes
const int speed = 5; // snowfall speed
 
// Duration of the animation
const thirtyMills = const Duration(milliseconds:30);
 
// Set the screen initial properties
CanvasRenderingContext2D ctx;
var W = window.innerWidth;
var H = window.innerHeight;
 
// Randomizer instance variable to be used later on with positions and sizes
var rng = new math.Random();

Following, I have the brains of my little animation. Everything happens here, from canvas set-up, to flake creation to positioning. I have put together lots of different steps on the same method here. This is almost a <abbr title=’Too long; didn’t read’ rel=’tooltip’>tl;dr</abbr> for code. I don’t want to bore anyone very semantic code. The idea here is to get people going. We all know separation of concerns and good semantics.

void snowFall(){
  // draw background on canvas
  ctx.fillStyle="#000";
  ctx.fillRect(0,0,W,H);
  
  //drawing intitial snowflakes on canvas
  ctx.fillStyle = "#fff";
  // rain 
  ctx.beginPath();
  
  for(var i = 0; i < maxFlakes; i++){
    var f = flakes[i];
    ctx.moveTo(f._x, f._y);
    ctx.arc(f._x, f._y, f._r, 0, math.PI*2, true);
  }
  ctx.fill();
  
  //moving snow flakes
  var angl=0;
  for(var i=0; i < maxFlakes; i++){
    angl+=0.1;
    var f=flakes[i];
    f._x +=math.sin(angl).abs() + 0.1 ;
    f._y += math.cos(angl).abs() * speed;
    //resetting snowflakes when they are out of frame
    if(f._x > W || f._x < 0 || f._y > H){
      f._x = rng.nextDouble() * W;
      f._y=-10.0;
    }
  }
  
}

Notice we use beginPath every time we call the snowFall() method. This is to make sure the path for each of our flakes get reset, and don’t end up drawing its entire path ton the screen. If we hadn’t used that, we would end up with something like:

Dart Rain

This could be useful if you wanted rain instead of snow 🙂

Finally on my main function, I do three things:

  1. I initialise my canvas object;
  2. create a pointer in memory for each of my flakes;
  3. make sure the animation runs every 30 milliseconds,
void main() {
  var canvas = querySelector('#stage');
  ctx = canvas.getContext('2d');
  
  // resize canvas
  canvas.width = W;
  canvas.height = H;
  
  // create random flakes
  for(int i = 0; i < maxFlakes; i++){
    flakes.add(
        new Flake(
          rng.nextDouble() * W,
          rng.nextDouble() * H,
          rng.nextDouble() * 3,
          rng.nextDouble() * maxFlakes
        )
    );
  }
    
  // make the magic happen every 30 milliseconds
  new Timer.periodic(thirtyMills, (Timer t) => snowFall());
}

You can find the entire code published on my GitHub account, and obviously fork it and send me pull requests if you think you can make it any better.

https://github.com/mplacona/snowy_dart