html http://gbrlgrct.com/gists/40699e763651c22754c0057f89151087/chispitas.html

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了html http://gbrlgrct.com/gists/40699e763651c22754c0057f89151087/chispitas.html相关的知识,希望对你有一定的参考价值。

<body style="margin:0"></body>
<script src="http://gbrlgrct.com/gists/07d74ba686ce04eccb11faa44f2ae229/functional.js"></script>
<script>
'use script';

// === SETUP

const SPACEW = document.body.clientWidth;
const SPACEH = document.body.clientHeight;

const canvas = document.createElement('canvas');
canvas.width = SPACEW;
canvas.height = SPACEH;
document.body.appendChild(canvas);

const ctx = canvas.getContext('2d');

// === MATHS

Math.TAU = 2 * Math.PI;

class Vec2 {
    constructor(x, y) {
        this.x = x || 0;
        this.y = y || 0;
    }

    clone() {
        return new Vec2(this.x, this.y);
    }

    distanceTo(v) {
        return v.clone().sub_(this).length();
    }

    lengthSq() {
        return this.x * this.x + this.y * this.y;
    }

    length() {
        return Math.sqrt(this.lengthSq());
    }

    norm() {
        return this.clone().sdiv_(this.length());
    }

    perpCW() {
        return new Vec2(-this.y, this.x);
    }

    perpCCW() {
        return new Vec2(this.y, -this.x);
    }

    add(v) {
        return this.clone().add_(v);
    }

    add_(v) {
        this.x += v.x;
        this.y += v.y;
        return this;
    }

    sub(v) {
        return this.clone().sub_(v);
    }

    sub_(v) {
        this.x -= v.x;
        this.y -= v.y;
        return this;
    }

    smul(k) {
        return this.clone().smul_(k);
    }

    smul_(k) {
        this.x *= k;
        this.y *= k;
        return this;
    }

    sdiv(k) {
        return this.clone().sdiv_(k);
    }

    sdiv_(k) {
        this.x /= k;
        this.y /= k;
        return this;
    }

    static zero() {
        return Vec2.fromXY(0, 0);
    }

    static fromXY(x, y) {
        return new Vec2(x, y);
    }

    static fromRads(r) {
        return Vec2.fromXY(Math.cos(r), Math.sin(r));
    }

    static fromAngle(a) {
        return Vec2.fromRads(a / 360 * Math.TAU);
    }
}

function uniformI(a, b) {
    return Math.floor(a + (b - a) * Math.random());
}

function randomHsla() {
    return randomHslaWithHue(uniformI(0, 360));
}

function randomHslaWithHue(hue) {
    return [hue, 100, uniformI(20, 80), 1];
}

function hslaToColor(hsla) {
    return `hsla(${hsla[0]}, ${hsla[1]}%, ${hsla[2]}%, ${hsla[3]})`;
}

function randomDirectionVec2() {
    return Vec2.fromRads(Math.random() * Math.TAU);
}

// === ENTITIES

function makeCircleDrawing(size, color) {
    var canvas = document.createElement('canvas');
    canvas.width = canvas.height = size * 2;
    var ctx = canvas.getContext('2d');
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.arc(size, size, size, 0, Math.TAU);
    ctx.fill();
    return canvas;
}

class Particle {
    constructor(pos, spd, hsla, trace, size) {
        this.deleted = false;
        this.active = false;
        this.trace = Boolean(trace);

        this.pos = pos.clone();
        this.spd = spd.clone();
        this.size = size || uniformI(2, 6);
        this.hsla = hsla;
        this.color = hslaToColor(hsla);

        this.path = [];

        this.canvas = makeCircleDrawing(this.size, this.color);
    }
}

class Force {
    constructor(pos, value, color, pulsate) {
        this.pos = pos.clone();
        this.value = value;
        this.color = color || (value < 0 ? 'blue' : 'white');
        this.pulsate = pulsate || null;
        this.pulsateState = -1.0 * Math.sign(value);
    }

    step() {
        const pulsateLimit = 30;
        this.value += this.pulsateState * 0.8;
        if (this.value >= pulsateLimit || this.value <= -pulsateLimit) {
            this.pulsateState *= -1;
        }
        this.color = hslaToColor([240, 100, 50 + 50 * Math.abs(pulsateLimit + this.value) / (pulsateLimit * 2), 1]);
    }
}

class ParticleGenerator {
    constructor(pos, value, color) {
        this.pos = pos.clone();
        this.value = value;
        this.color = color || 'orange';
    }
}

// === STATE

let particles = [];

let forces = [];

let mousePositions = [];

let particleGenerators = [];

let mousePosition = Vec2.zero();

let mouseSpeed = Vec2.zero();

let shouldProduce = false;

let shiftPressed = false;

let controlPressed = false;

let altPressed = false;

let clickDown = null;

let friction = false;

let showForceGens = true;

let currentHue = null;

const COMPOSITE_OPERATION_DEFAULT = 'source-over';

let compositeOperation = COMPOSITE_OPERATION_DEFAULT;

const SINK_RADIUS = 10;

let showParticleSpeedVector = false;

// === HELPERS

function produceParticlesAtPos(bag, pos, n) {
    for (let i = 0; i < n; ++i) {
        const spd = randomDirectionVec2().smul_(5 * Math.random() + 5);
        bag.push(new Particle(pos, spd, randomHsla()));
    }
}

function updateMouseSpeed(pos) {
    mousePositions.push(pos);

    if (mousePositions.length === 11) {
        mousePositions.shift();
    }

    const n = mousePositions.length;
    let spd = Vec2.zero();

    for (var i = 1; i < n; ++i) {
        spd.add_(mousePositions[i]);
        spd.sub_(mousePositions[i - 1]);
    }

    spd.sdiv_(n);

    mouseSpeed = spd;
}

function cleanOutOfBounds(bag) {
    var nP = bag.length;
    var ds = 0;
    for (var iP = 0; iP < nP; ++iP) {
        var p = bag[iP];
        if (p.deleted) {
            ds += 1;
            continue;
        }
        if (!checkBounds(p)) {
            p.deleted = true;
        }
    }
    if (ds > 5000) {
        return bag.filter(p => !p.deleted);
    }
    return null;
}

function checkBounds(subject) {
    return subject.pos.x >= 0 &&
           subject.pos.y >= 0 &&
           subject.pos.x <= SPACEW &&
           subject.pos.y <= SPACEH;
}

// === EVENTS

window.addEventListener('mousemove', function (ev) {
    const pos = Vec2.fromXY(ev.clientX, ev.clientY);

    if (clickDown === 1 && particles.length > 0) {
        const lastParticle = particles[particles.length - 1];

        if (pos.distanceTo(lastParticle.pos) > 25) {
            let hsla = randomHslaWithHue(lastParticle.hsla[0]);
            particles.push(new Particle(pos, Vec2.zero(), hsla, true));
        }
    } else if (clickDown === 2 && forces.length > 0) {
        if (controlPressed) {
            const axL = mouseSpeed.perpCW().norm();
            const axR = mouseSpeed.perpCCW().norm();
            const f = 20;
            forces.push(new Force(pos.add(axL.smul(10)), -f));
            forces.push(new Force(pos.add(axL.smul(5)), f));
            forces.push(new Force(pos.add(axR.smul(5)), f));
            forces.push(new Force(pos.add(axR.smul(10)), -f));
        } else {
            const lastForce = forces[forces.length - 1];
            if (pos.distanceTo(lastForce.pos) > 25) {
                forces.push(new Force(pos, lastForce.value));
            }
        }
    }

    mousePosition = pos;
});

window.addEventListener('mousedown', function (ev) {
    const pos = Vec2.fromXY(ev.clientX, ev.clientY);

    if (ev.button === 0) {
        if (shiftPressed) {
            particleGenerators.push(new ParticleGenerator(pos, 2));
        } else {
            shouldProduce = true;
        }
    } else if (ev.button === 1) {
        particles.push(new Particle(pos, Vec2.zero(), randomHsla(), true));
    } else if (ev.button === 2) {
        let forceValue = 10;
        let pulsate = null;
        if (shiftPressed) {
            forceValue *= -1;
        }
        if (altPressed) {
            pulsate = 'linear';
        }
        forces.push(new Force(pos, forceValue, null, pulsate));
    }

    clickDown = ev.button;
});

window.addEventListener('mouseup', function (ev) {
    if (ev.button === 0) {
        shouldProduce = false;
    }
    particles.filter(p => p.trace).forEach(p => p.active = true);
    clickDown = null;
});

window.addEventListener('keydown', function (ev) {
    if (ev.keyCode === 16) { // shift
        shiftPressed = true;
    } else if (ev.keyCode === 17) { // control
        controlPressed = true;
    } else if (ev.keyCode === 18) { // alt
        altPressed = true;
    } else if (ev.keyCode === 32) { // space
        particles = [];
    } else if (ev.keyCode === 70) { // f
        friction = !friction;
    } else if (ev.keyCode === 68) { // d
        forces = forces.filter(f => f.pos.distanceTo(mousePosition) > SINK_RADIUS);
        particleGenerators = particleGenerators.filter(f => f.pos.distanceTo(mousePosition) > SINK_RADIUS);
    } else if (ev.keyCode === 72) { // h
        showForceGens = !showForceGens;
    } else if (ev.keyCode === 67) { // c
        compositeOperation = prompt('Composite operation', compositeOperation) || COMPOSITE_OPERATION_DEFAULT;
    } else if (ev.keyCode === 90) { // z
        forces = forces.slice(0, forces.length - 1);
    } else if (ev.keyCode === 86) { // v
        showParticleSpeedVector = !showParticleSpeedVector;
    }
});

window.addEventListener('keyup', function (ev) {
    if (ev.keyCode === 16) { // shift
        shiftPressed = false;
    } else if (ev.keyCode === 17) { // control
        controlPressed = false;
    } else if (ev.keyCode === 18) { // alt
        altPressed = false;
    }
});

canvas.addEventListener('contextmenu', function (ev) {
    ev.preventDefault();
});

setInterval(function () {
    updateMouseSpeed(mousePosition);
    if (shouldProduce) {
        produceParticlesAtPos(particles, mousePosition, uniformI(10, 20));
    }
    particleGenerators.forEach(gen => {
        produceParticlesAtPos(particles, gen.pos, uniformI(1, gen.value));
    });
}, 16);

// clean up out-of-bounds particles
setInterval(function () {
    var newParticles = cleanOutOfBounds(particles);
    if (newParticles) {
        particles = newParticles;
    }
}, 5000)

// === DRAWING

function applyGravity(sink, subject) {
    const dpos = sink.pos.sub(subject.pos);
    const dd = dpos.length();

    if (dd <= SINK_RADIUS) {
        return;
    }

    const ir2 = Math.pow(dd, 2);

    const vel = dpos.sdiv(dd).smul(100 * sink.value / ir2);

    subject.spd.add_(vel);
}

function applyFriction(subject) {
    subject.spd.add_(subject.spd.smul(-0.05));
}

function drawVector(ctx, pos, vec, scale, color) {
    const vecS = pos.add(vec.smul(scale));
    ctx.beginPath();
    ctx.moveTo(pos.x, pos.y);
    ctx.lineTo(vecS.x, vecS.y);
    ctx.strokeStyle = color;
    ctx.lineWidth = 2
    ctx.stroke();
}

function simulate() {
    var nP = particles.length;
    var nF = forces.length;
    for (var iP = 0; iP < nP; ++iP) {
        var particle = particles[iP];

        if (particle.deleted) {
            continue;
        }

        if (particle.trace && !particle.active) {
            continue;
        }

        particle.pos.add_(particle.spd);

        for (var iF = 0; iF < nF; ++iF) {
            applyGravity(forces[iF], particle);
        }

        if (friction) {
            applyFriction(particle);
        }

        if (particle.trace) {
            particle.path.push(particle.pos.clone());
        }
    }
}

function clearScreen() {
    ctx.globalCompositeOperation = 'source-over';
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, SPACEW, SPACEH);
}

function drawPath(path, color) {
    ctx.beginPath();
    ctx.moveTo(path[0].x, path[0].y);
    var nXY = path.length;
    for (var iXY = 0; iXY < nXY; ++iXY) {
        var p = path[iXY];
        ctx.lineTo(p.x, p.y);
    }
    ctx.strokeStyle = color;
    ctx.lineWidth = 2;
    ctx.stroke();
}

function drawParticles() {
    var nP = particles.length;
    for (var iP = 0; iP < nP; ++iP) {
        var particle = particles[iP];

        if (particle.deleted) {
            continue;
        }

        var sz = +particle.size;
        ctx.drawImage(particle.canvas, particle.pos.x - sz, particle.pos.y - sz);

        if (particle.trace && particle.active) {
            drawPath(particle.path, particle.color);
        }

        if (showParticleSpeedVector) {
            drawVector(ctx, particle.pos, particle.spd, 5, particle.color);
        }
    }
}

function drawForces() {
    var nF = forces.length;
    for (var iF = 0; iF < nF; ++iF) {
        var force = forces[iF];
        ctx.fillStyle = force.color;
        ctx.beginPath();
        ctx.arc(force.pos.x, force.pos.y, SINK_RADIUS, 0, Math.TAU);
        ctx.fill();
    }
}

function applyForcePulsation() {
    var nF = forces.length;
    for (var iF = 0; iF < nF; ++iF) {
        var force = forces[iF];
        if (force.pulsate) {
            force.step();
        }
    }
}

function drawParticleGenerators() {
    var nF = particleGenerators.length;
    for (var iF = 0; iF < nF; ++iF) {
        var gen = particleGenerators[iF];
        ctx.fillStyle = gen.color;
        ctx.beginPath();
        ctx.arc(gen.pos.x, gen.pos.y, SINK_RADIUS, 0, Math.TAU);
        ctx.fill();
    }
}

function draw() {
    clearScreen();

    ctx.globalCompositeOperation = compositeOperation;

    drawParticles();

    if (showForceGens) {
        drawForces();
        drawParticleGenerators();
    }

    applyForcePulsation();

    // drawVector(ctx, mousePosition, mouseSpeed, 5, 'green');
}

function loop() {
    simulate();
    draw();
    requestAnimationFrame(loop);
}

// === GO !

loop();

</script>

以上是关于html http://gbrlgrct.com/gists/40699e763651c22754c0057f89151087/chispitas.html的主要内容,如果未能解决你的问题,请参考以下文章

html http://gbrlgrct.com/gists/0ec9f597e2c9b37fc868/pi.html

html http://gbrlgrct.com/gists/08640e24c907cc627eca/webgl-mandelbrot.html

html http://gbrlgrct.com/gists/a48a2a7ddb6b38d52194/starry.html

html http://gbrlgrct.com/gists/deec0c0c12e63a94515b/burn.html

html http://gbrlgrct.com/gists/53e9405a69ad5922c3ed/mandelbrot.html

html http://gbrlgrct.com/gists/cc5ea4bf19838a41e527/cardiod.html