SOURCE

console 命令行工具 X clear

                    
>
console
let ctx = canvas.getContext('2d'),
    width = canvas.width = window.innerWidth,   
    height = canvas.height = window.innerHeight,
    particles = [];

let settings = {
    speed: 1,
    lifeTime: 1000,
    maxParticles: 50,
    lineLength: 35,
    radius: 0.6
};

function rotate(vec, angle) {
    // convert the angle from degrees to radians
    angle = angle * Math.PI / 180;
    return [(vec[0] * Math.cos(angle)) - (vec[1] * Math.sin(angle)), (vec[0] * Math.sin(angle)) + (vec[1] * Math.cos(angle))];
}

// 方向
let dirVecs = [
    [1, 0],
    rotate([1, 0], 120),
    rotate([1, 0], 240)
];

function particle() {
    this.x = 0;
    this.y = 0;
    this.age = 0;
    // 从方向里随机取一个值,0-2
    this.dir = dirVecs[Math.floor(Math.random() * 3)];
    // 时间随机颜色
    // H:0(或360)表示红色,120表示绿色,240表示蓝色,也可取其他数值来指定颜色。取值为:0 - 360。
    // S:取值为:0.0% - 100.0%;0% 意味着灰色,而 100% 是全彩
    // L:取值为:0.0% - 100.0%;0% 是黑色,100% 是白色。
    this.color = 'hsl(' + ((Date.now() / 240.0) % 360.0) + ', 90%, light%)';
}

particle.prototype.updateAndDraw = function() {
    this.age++;
    // 滚动时间能整除边长 则表示要转向了
    if (this.age % settings.lineLength === 0) {
        let dir1 = rotate(this.dir, 60);
        let dir2 = rotate(this.dir, -60);

        let options = [];

        options = [dir1, dir2];

        this.dir = options[Math.floor(Math.random() * options.length)];
    }

    ctx.fillStyle = this.color.replace('light', '40');
    ctx.beginPath();
    // 画圆
    ctx.arc(width / 2.0 + this.x, height / 2.0 + this.y, settings.radius, 0, 6.3);

    ctx.shadowBlur = settings.radius * 6;
    ctx.shadowColor = this.color.replace('light', '40');
    ctx.fill();

    this.x += this.dir[0] * settings.speed;
    this.y += this.dir[1] * settings.speed;
};


function updateAndDraw() {
    ctx.shadowBlur = 0;
    ctx.globalCompositeOperation = 'source-over';

    ctx.fillStyle = 'rgb(0, 0, 0, 0.03)';
    ctx.fillRect(0, 0, width, height);
    ctx.globalCompositeOperation = 'lighter';

    for (let i = particles.length - 1; i >= 0; i--) {
        // 移动粒子
        particles[i].updateAndDraw();
        if (particles[i].age > settings.lifeTime) {
            // remove the particle if it is to old
            particles.splice(i, 1);
        }
    }

    if (particles.length < settings.maxParticles) {
        // if more particles can be added
        if (Math.random() > 0.9) {
            particles.push(new particle());
        }
    } else if (particles.length > settings.maxParticles) {
        // if there are two many particles
        particles.splice(0, settings.maxParticles);
    }

    requestAnimationFrame(updateAndDraw);
}

function onResize() {
    width = canvas.width = window.innerWidth,
    height = canvas.height = window.innerHeight;
		
    // particles.length = 0; // 重加载的时候是否需要清除粒子树
    ctx.fillStyle = '#111';
    ctx.fillRect(0, 0, width, height);
}


function init() {
    ctx.fillStyle = '#111';
    ctx.fillRect(0, 0, width, height);

    window.onresize = onResize;

    updateAndDraw();
}

init();

<canvas id="canvas"></canvas>
html, body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  box-sizing: border-box;
}
#canvas {
    display: block;
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
}