SOURCE

console 命令行工具 X clear

                    
>
console
'use strict';

var maxParticles = 2000,
    particleSize = 1,
    emissionRate = 100,
    objectSize = 0;

var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

function Particle(point, velocity, acceleration) {
  this.position = point || new Vector(0, 0);
  this.velocity = velocity || new Vector(0, 0);
  this.acceleration = acceleration || new Vector(0, 0);
  this.color = 'rgb('+getRandomInt(0,255)+','+getRandomInt(0,255)+','+getRandomInt(0,255)+')';
  this.size = getRandomInt(1,5);
}

Particle.prototype.move = function () {
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);
};

function Vector(x, y) {
  this.x = x || 0;
  this.y = y || 0;
}

Vector.prototype.add = function (vector) {
  this.x += vector.x;
  this.y += vector.y;
}

Vector.prototype.getMagnitude = function () {
  return Math.sqrt(this.x * this.x + this.y * this.y);
};

Vector.prototype.getAngle = function () {
  return Math.atan2(this.y, this.x);
};

Vector.fromAngle = function (angle, magnitude) {
  return new Vector(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
};

function Emitter(point, velocity, spread) {
  this.position = point;
  this.velocity = velocity;
  this.spread = spread || Math.PI;
  this.drawColor = "#fff";
}

Emitter.prototype.emitParticle = function () {
  var angle = this.velocity.getAngle() + this.spread - (Math.random() * this.spread * 2);

  var magnitude = this.velocity.getMagnitude();

  var position = new Vector(this.position.x, this.position.y);

  var velocity = Vector.fromAngle(angle, magnitude);

  return new Particle(position, velocity);
};

function addNewParticles() {
  if (particles.length > maxParticles) return;

  for (var i = 0; i < emitters.length; i++) {
    for (var j = 0; j < emissionRate; j++) {
      particles.push(emitters[i].emitParticle());
    }
  }
}

function plotParticles(boundsX, boundsY) {
  var currentParticles = [];

  for (var i = 0; i < particles.length; i++) {
    var particle = particles[i];
    var pos = particle.position;

    if (pos.x < 0 || pos.x > boundsX || pos.y < 0 || pos.y > boundsY) continue;

    particle.move();
    currentParticles.push(particle);
  }

  particles = currentParticles;
}

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function drawParticles() {
  for (var i = 0; i < particles.length; i++) {
    var position = particles[i].position;
    ctx.fillStyle = particles[i].color;
    ctx.fillRect(position.x, position.y, particles[i].size, particles[i].size);
  }
}

function drawCircle(object) {ctx.fillStyle = object.drawColor;
  ctx.beginPath();
  ctx.arc(object.position.x, object.position.y, objectSize, 0, Math.PI * 2);
  ctx.closePath();
  ctx.fill();
}

var particles = [];

var emitters = [
  new Emitter(new Vector(canvas.width / 2, canvas.height / 2), Vector.fromAngle(0, 2))
];

function loop() {
  clear();
  update();
  draw();
  queue();
}

function clear() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
}

function update() {
  addNewParticles();
  plotParticles(canvas.width, canvas.height);
}

function draw() {
  drawParticles();
  emitters.forEach(drawCircle);
}

function queue() {
  window.requestAnimationFrame(loop);
}

loop();
<canvas></canvas>
* {margin: 0; padding: 0}
body {background: #000; overflow: hidden}