链式反射特效

代码语言:html

所属分类:动画

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">

    <title> chain reaction</title>
    <style>
        body {
            font-family: Arial, Helvetica, "Liberation Sans", FreeSans, sans-serif;
            background-color: #000;
            margin: 0;
            padding: 0;
            border-width: 0;
        }
    </style>

</head>
<body translate="no">

    <script>
        window.addEventListener("load", function () {

            const gridStep = 40; // grid step (distance between atoms - center to center)
            const radiusToStepRatio = 0.3; // radius to step ratio, didn't guess ? < 0.5
            const activity = 3; // maximum number of atoms destroyed by one particle

            const atomRadius = gridStep * radiusToStepRatio;

            let canv, ctx; // canvas and drawing context for atoms
            let canvP, ctxP; // canvas and drawing context for particles

            let maxx, maxy; // canvas size

            let orgx, orgy; // position of the center of top left atom

            let nbx, nby; // nb of atoms horiz. / vert.
            let crystal; // matrix of atoms

            let explosions; // table of disintegrations to come / in progress
            let endsOfExplosion; // list of elements to be removed from explosions
            let requestID; // to cancel animation in progress
            let lRay; // maximum length for particle path
            let nbEff = 0;
            // shortcuts for Math functions

            const mrandom = Math.random;
            const mfloor = Math.floor;
            const mround = Math.round;
            const mceil = Math.ceil;
            const mtrunc = Math.trunc;
            const mabs = Math.abs;
            const mmin = Math.min;
            const mmax = Math.max;

            const mPI = Math.PI;
            const mPIS2 = Math.PI / 2;
            const m2PI = Math.PI * 2;
            const msin = Math.sin;
            const mcos = Math.cos;
            const matan2 = Math.atan2;

            const mhypot = Math.hypot;
            const msqrt = Math.sqrt;

            //-----------------------------------------------------------------------------
            // miscellaneous functions
            //-----------------------------------------------------------------------------

            function alea(min, max) {
                /* random float min..max
                             or 0..min if max is undefined
                           min and max are supposed to be numbers, max (if provided) > min
                           */
                if (typeof max == 'undefined') return min * mrandom();
                return min + (max - min) * mrandom();
            } // alea

            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            function intAlea(min, max) {
                /*
                                with 2 parameters :
                                  returns random integer min..max - 1
                                else
                                  returns random integer 0..min - 1
                              min and max are supposed to be integers, max (if provided) > min
                              */

                if (typeof max == 'undefined') {
                    max = min; min = 0;
                }
                return mfloor(min + (max - min) * mrandom());
            } // intAlea

            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            function distance(p0, p1) {

                /* distance between two points */

                return mhypot(p0[0] - p1[0], p0[1] - p1[1]);

            } // function distance

            //------------------------------------------------------------------------
            /* class Atom */

            function Atom(kx, ky) {
                this.kx = kx;
                this.ky = ky;
                this.x = orgx + kx * gridStep;
                this.y = orgy + ky * gridStep;
                this.state = 0; // normal state - till now
                this.emitted = false;

            } // Atom

            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            Atom.prototype.explode = function () {

                if (this.state != 0) return false; // inform caller explosion already in progress

                this.state = 1; // new state for atom
                explosions.push(this); // put atom into animation queue
                return true; // inform caller this is a new explosion
            }; // Atom.prototype.explode

            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            Atom.prototype.draw = function (radius, color, clearBefore) {

                // use color = undefined  && clearBefore = true to completely erase atom

                if (clearBefore) {
                    ctx.clearRect(this.x - gridStep / 2, this.y - gridStep / 2, gridStep, gridStep);
                }
                if (color) {
                    ctx.beginPath();
                    ctx.fillStyle = color;
                    ctx.arc(this.x, this.y, radius, 0, m2PI);
                    ctx.fill();
                }
            }; // Atom.prototype.draw

            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            Atom.prototype.drawParticlePath = function (color, thickness, length) {

                ctxP.beginPath();
                ctxP.strokeStyle = color;
                ctxP.lineWidth = thickness;
                ctxP.moveTo(this.x, this.y);
                ctxP.lineTo(this.x + length * this.c, this.y + length * this.s);
                ctxP.stroke();
            }; // Atom.prototype.drawParticlePath

            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            Atom.prototype.animate = function (time, idx) {
                /* idx = index of this atom in table 'explosions'
                                                    May (and will) change between two calls for same Atom.
                                                    Used to declare end of animation by pushing it onto 'endsOfExplosion'.
                                                 */

                let elapsed, left;

                switch (this.state) {
                    case 1: // beginning of explosion
                        this.tRef = time; // reference time : start of disintegration
                        this.hue = alea(360);
                        this.color = `hsl(${this.hue}, 100%, 50%)`;

                        this.th = alea(m2PI); // random direction of explosion
                        this.c = mcos(this.th); // cosine and sine of direction
                        this.s = msin(this.th);
                        this.currRadius = 1.5 * atomRadius;
                        if (this.currRadius > gridStep / 2 - 1) this.currRadius = gridStep / 2 - 1;

                        this.duration = alea(800, 1000);
                        this.emitTime = this.tRef + 0.6 * this.duration;
                        this.pathThickness = 3;
                        this.pathLength = lRay;
                        this.pathDuration = alea(300, 500);
                        if (this.emitTime + this.pathDuration < this.tRef + this.duration) {
                            /* be sure this.pathDuration is long enough to last longer than this.duration */
                            this.pathDuration = this.tRef + this.duration - this.emitTime + 1; // + 1 is ridiculous, but I said longer…
                        }

                        this.pathLum = 80; // % Lightness for path color
                        this.draw(this.currRadius, this.color, true);
                        ++this.state;
                        return;

                        case 2: // atom oscillation - particle path illumination

                            elapsed = time - this.emitTime; // time to emit particle ?
                            if (elapsed > 0) {
                                left = (this.pathDuration - elapsed) / this.pathDuration;
                                this.drawParticlePath(`hsl(${this.hue}, 100% ,${20 + (this.pathLum - 20) * left}%)`, this.pathThickness * left, this.pathLength);
                                if (!this.emitted) {
                                    // emission of particle
                                    // compute hit atoms
                                    this.computeHit();
                                    this.emitted = true;
                                }
                            } // if particle emission in progress

                            elapsed = time - this.tRef;
                            if (elapsed >= this.duration) {
                                this.draw(0, undefined, true);
                                ++this.state; // next step in animation
                                nbEff++;
                                return;
                            }

                            // draw atom in current state
                            left = (this.duration - elapsed) / this.duration;
                            thi.........完整代码请登录后点击上方下载按钮下载查看

网友评论0