<canvas id="canvas"></canvas>
body,html{
  padding: 0;
  margin: 0;
}
class Boid{
    constructor(ctx,width,height){
        this.ctx = ctx;
        this.x = width * Math.random();
        this.y = height * Math.random();
        this.vx = 0;
        this.vy = 0;
        this.rad = 2;
        this.bColor = '#FFFFFF';
    }
    draw(){
        this.ctx.fillStyle = this.bColor;
        this.ctx.beginPath();
        this.ctx.arc(this.x, this.y, this.rad, 0, 2 * Math.PI, false);
        this.ctx.fill();
        this.ctx.closePath();
    }
}
class Boids{
    constructor(){
        this.boids = [];
        this.NUM_BOIDS = 100;
        this.BOID_SIDE = 5;
        this.MAX_SPEED = 7;
        this.BG_COLOR = '#333333';
        this.w = window.innerWidth;
        this.h = window.innerHeight;
        this.canvas = document.getElementById("canvas");
        this.ctx = this.canvas.getContext('2d');
        this.canvas.width = this.w;
        this.canvas.height = this.h;
        this.init();
    }
    init(){
        for (let i = 0; i < this.NUM_BOIDS; i++) {
            let boid = new Boid(this.ctx,this.w,this.h);
            this.boids.push(boid);
        }
    }
    loop(){
        requestAnimationFrame(this.loop.bind(this));
        this.ctx.fillStyle = this.BG_COLOR;
        this.ctx.fillRect(0, 0, this.w, this.h);
        for (let i = 0; i < this.boids.length; i++) {
            this.cohesion(i);
            this.separation(i);
            this.alignment(i)
            let b = this.boids[i];;
            let speed = Math.sqrt(b.vx * b.vx + b.vy * b.vy);
            if (speed >= this.MAX_SPEED) {
                let r = this.MAX_SPEED / speed;
                b.vx *= r;
                b.vy *= r;
            }
            //領域内の判定
            if (b.x < 0 && b.vx < 0 || b.x > this.w && b.vx > 0) b.vx *= -1;
            if (b.y < 0 && b.vy < 0 || b.y > this.h && b.vy > 0) b.vy *= -1;
            b.x += b.vx;
            b.y += b.vy;
            b.draw();
        }
    }
    //結合
    cohesion(index) {
        let c = {
            x: 0,
            y: 0
        };
        for (let i = 0; i < this.boids.length; i++) {
            if (i !== index) {
                c.x += this.boids[i].x;
                c.y += this.boids[i].y;
            }
        }
        c.x /= this.NUM_BOIDS - 1;
        c.y /= this.NUM_BOIDS - 1;

        this.boids[index].vx += (c.x - this.boids[index].x) / 100;
        this.boids[index].vy += (c.y - this.boids[index].y) / 100;
    }
    //引き離し
    separation(index) {
        for (let i = 0; i < this.boids.length; i++) {
            let d = this.getDistance(this.boids[i], this.boids[index]);
            if (d < this.BOID_SIDE) {
                this.boids[index].vx -= this.boids[i].x - this.boids[index].x;
                this.boids[index].vy -= this.boids[i].y - this.boids[index].y;
            }
        }
    }
    //整列
    alignment(index) {
        let al = {
            x: 0,
            y: 0
        };
        for (let i = 0; i < this.boids.length; i++) {
            if (i !== index) {
                al.x += this.boids[i].vx;
                al.y += this.boids[i].vy;
            }
        }
        al.x /= this.NUM_BOIDS - 1;
        al.y /= this.NUM_BOIDS - 1;
        this.boids[index].vx += (al.x - this.boids[index].vx) / 8;
        this.boids[index].vy += (al.y - this.boids[index].vy) / 8;
    }
    getDistance(p1, p2) {
        let dx = p1.x - p2.x;
        let dy = p1.y - p2.y;
        return Math.sqrt(dx * dx + dy * dy);
    }
}
let boids = new Boids();
boids.loop();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.