Posted in css animations
6641
7:00 am, May 26, 2023

Animated Triangles on Canvas JS HTML

Cool looking triangles animation using just canvas and js. 

Explanation of Code

  1. The code starts with the statement 'use strict'; which enables strict mode, a way to introduce better error-checking and prevent the use of certain error-prone features in JavaScript.

  2. The code sets up an event listener for the load event on the window object. This event fires when the entire page and its associated resources have finished loading.

  3. There are several variables declared using the let keyword, such as triWidth, triHeight, firstRun, canv, ctx, maxx, maxy, lRef, grid, nbx, nby, hnbx, hnby, blocks, nbLines, and events. These variables will be used throughout the code to store and manipulate various values.

  4. Following the variable declarations, there are a series of constants defined using the const keyword. These constants represent mathematical values and functions such as mrandom, mfloor, mround, mceil, mabs, mmin, mmax, mPI, mPIS2, m2PI, msin, mcos, matan2, mhypot, msqrt, rac3, rac3s2, and mPIS3. These constants provide shortcuts for commonly used mathematical operations.

  5. The code then defines a function called alea(mini, maxi) which generates a random number within a given range.

  6. Next, there is a function called intAlea(mini, maxi) which generates a random integer within a given range.

  7. Another function called arrayShuffle(array) is defined, which shuffles the order of items in an array.

  8. The code includes a function called lerp(p0, p1, alpha) which calculates a linear interpolation point between two points based on a given alpha value.

  9. The addP(text) function creates a new <p> element and appends it to an element with the id txtexplain.

  10. The code defines a class called Triangle, which represents a triangle in the grid. It includes various methods for setting up and manipulating triangles.

  11. There is a function called createGrid() which creates a grid of triangles.

  12. The code defines a function called createBlock(tri, side) which creates a block of connected triangles based on a starting triangle and a side.

  13. The createBlocks() function creates blocks of triangles in the grid.

  14. The orientBlock(block) function orients the triangles in a block based on their entry and exit points.

  15. Lastly, there are functions drawBlock(block, alpha, hue, light, widthCoeff) and hatchaab(alpha, hue, lineWidth, lum) which are responsible for drawing and rendering the triangles and blocks on a canvas.

 

Animated Triangles on Canvas JS HTML Demo

View Demo Full Screen View Demo New Tab

Animated Triangles on Canvas JS HTML Code

HTML

<div id=explain>
  <div id="txtexplain"></div>
  <p class=buttline><button type="button" id="butt2">next &gt;&gt;</button> <button type="button" id="butt3">skip it
      all</button></p>
</div>

CSS

body {
  font-family: Arial, Helvetica, "Liberation Sans", FreeSans, sans-serif;
  background-color: #000;
  margin: 0;
  padding: 0;
  border-width: 0;
}

#explain {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 10px;
  background-color: rgba(255, 255, 255, 0.9);
  border: 1px solid black;
  border-radius: 10px;
  padding: 10px;
  color: black;
  z-index: 1;
}

.hidden {
  display: none;
}

#explain p.buttline {
  text-align: center;
}

Javascript

"use strict";

window.addEventListener("load", function () {
  let triWidth, triHeight; // length of triangle side and altitude
  let firstRun = !location.pathname.includes("/fullcpgrid/");
  let canv, ctx; // canvas and context

  let maxx, maxy, lRef; // canvas dimensions (lRef is average)

  let grid;
  let nbx, nby;
  let hnbx, hnby; // number of triangles in the half of the width, height of the canvas
  let blocks, nbLines;
  let events;

  // shortcuts for Math.
  const mrandom = Math.random;
  const mfloor = Math.floor;
  const mround = Math.round;
  const mceil = Math.ceil;
  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;

  const rac3 = msqrt(3);
  const rac3s2 = rac3 / 2;
  const mPIS3 = Math.PI / 3;

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

  function alea(mini, maxi) {
    // random number in given range

    if (typeof maxi == "undefined") return mini * mrandom(); // range 0..mini

    return mini + mrandom() * (maxi - mini); // range mini..maxi
  }
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function intAlea(mini, maxi) {
    // random integer in given range (mini..maxi - 1 or 0..mini - 1)
    //
    if (typeof maxi == "undefined") return mfloor(mini * mrandom()); // range 0..mini - 1
    return mini + mfloor(mrandom() * (maxi - mini)); // range mini .. maxi - 1
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function arrayShuffle(array) {
    /* randomly changes the order of items in an array
           only the order is modified, not the elements
        */
    let k1, temp;
    for (let k = array.length - 1; k >= 1; --k) {
      k1 = intAlea(0, k + 1);
      temp = array[k];
      array[k] = array[k1];
      array[k1] = temp;
    } // for k
    return array;
  } // arrayShuffle

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

  /* returns lerp point between p0 and p1,
        alpha = 0 will return p0, alpha = 1 will return p1
        values of alpha outside [0,1] may be used to compute points outside the p0-p1 segment
      */
  function lerp(p0, p1, alpha) {
    return {
      x: (1 - alpha) * p0.x + alpha * p1.x,
      y: (1 - alpha) * p0.y + alpha * p1.y
    };
  } // function lerp

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

  function addP(text) {
    let p = document.createElement("p");
    p.append(text);
    txtexplain.append(p);
  }

  //------------------------------------------------------------------------
  class Triangle {
    /* numbering of vertices / edges

              0                        2---1---1
             / \                        \     /
            2   0                        2   0
           /     \                        \ /
          2---1---1                        0
  */

    constructor(kx, ky) {
      this.kx = kx;
      this.ky = ky;
      this.kxc = kx - hnbx;
      this.kyc = ky - hnby;
      this.upsideDown = (this.kxc + this.kyc) & 1; // 0 or 1
      this.setXY();
    }

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

    setXY() {
      let xa, ya, vertices, deltay, upsideDown;

      // centre of this triangle (middle of height, not gravity centre)
      this.ya = ya = maxy / 2 + this.kyc * triHeight;
      this.xa = xa = maxx / 2 + (this.kxc * triWidth) / 2;

      this.vertices = vertices = [];
      deltay = triHeight / 2;
      if (this.upsideDown) deltay = -deltay;

      vertices[0] = { x: xa, y: ya - deltay };
      vertices[1] = { x: xa + triWidth / 2, y: ya + deltay };
      vertices[2] = { x: xa - triWidth / 2, y: ya + deltay };
    }
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    setBase(side) {
      // where side is 0,1,2
      this.base = side; // which side is the base ?
      this.dirBase = [
        [0, 1, 2],
        [2, 1, 0]
      ][this.upsideDown][this.base]; // orientation of base (== base side number for normally oriented triangle)
    }

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

    setNeighbors() {
      /* to be called only after the whole grid has been created */
      let kx, ky;
      this.neighbors = [];
      for (let k = 0; k < 3; ++k) {
        kx = this.kx + [1, 0, -1][k];
        ky =
          this.ky +
          [
            [0, 1, 0],
            [0, -1, 0]
          ][this.upsideDown][k];
        this.neighbors[k] =
          kx < 0 || kx >= nbx || ky < 0 || ky >= nby ? false : grid[ky][kx];
      } // for k
    } // Triangle.setNeighbor

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    setOneaab(side, value) {
      // sets value of this.aab[side] and the matching neighbor (if any) too
      // sets the "base" property to this side if value is 1
      this.aab[side] = value;
      if (this.neighbors[side]) {
        this.neighbors[side].aab[2 - side] = value;
      }
      if (value == 1) {
        this.setBase(side);
        if (this.neighbors[side]) {
          this.neighbors[side].setBase(2 - side);
        }
      }
    }

    setaab() {
      /* sets 3 values of this.aab, taking into account the already filled values and the constraints relative to the neighborhood
       */
      let neigh, possible, choice;
      const zeroes = [];
      const ones = [];
      const empty = [];

      // count 1s and 0s already present
      for (let k = 0; k < 3; ++k) {
        if (this.aab[k] === 0) zeroes.push(k);
        else if (this.aab[k] === 1) ones.push(k);
        else empty.push(k);
      }

      if (empty.length == 0) {
        // already completed
        if (ones.length != 1) throw "wtf ???";
        return; // already completed, ok
      }
      if (ones.length > 1) throw "wtf ???";

      if (ones.length == 1) {
        // already a 1, no choice
        empty.forEach((s) => this.setOneaab(s, 0));
        return;
      }
      // no "1" already present. Check empty edges to check if 1 are possible
      possible = [];
      empty.forEach((s) => {
        neigh = this.neighbors[s];
        if (!neigh || !neigh.aab.includes(1)) possible.push(s);
      });
      if (possible.length == 0) throw "impossible to add a 1";
      choice = possible[intAlea(possible.length)];
      this.setOneaab(choice, 1);
      empty.splice(empty.indexOf(choice), 1);
      // fill the rest with 0
      while (empty.length > 0) {
        choice = empty.pop();
        this.setOneaab(choice, 0);
      }
    } // setaab
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    drawaab(lineWidth, opacity) {
      ctx.beginPath();
      this.aab.forEach((v, k) => {
        ctx.beginPath();
        ctx.moveTo(this.vertices[k].x, this.vertices[k].y);
        ctx.lineTo(this.vertices[(k + 1) % 3].x, this.vertices[(k + 1) % 3].y);
        ctx.lineWidth = lineWidth;
        ctx.strokeStyle = `hsla(${[120, 0][v]},100%,50%,${opacity})`;
        ctx.stroke();
      });
    } // draw aab

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    hatchaab(alpha, hue, lineWidth, lum = 50) {
      // for alpha = 0..1
      const pa = lerp(this.vertices[this.a], this.vertices[this.b], alpha);
      const pb = lerp(this.vertices[this.c], this.vertices[this.b], alpha);
      ctx.beginPath();
      ctx.moveTo(pa.x, pa.y);
      ctx.lineTo(pb.x, pb.y);
      ctx.lineWidth = lineWidth;
      ctx.strokeStyle = `hsl(${hue},100%,${lum}%)`;
      ctx.stroke();
    }
  } // class Triangle

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

  function createGrid() {
    let kx1, ky1, cell;
    let stackDist = [];
    grid = [];

    for (let ky = 0; ky < nby; ++ky) {
      grid[ky] = [];
      for (let kx = 0; kx < nbx; ++kx) {
        grid[ky][kx] = new Triangle(kx, ky);
      } // for kx
    } // for ky

    grid.forEach((line) => line.forEach((tri) => tri.setNeighbors()));
    grid.forEach((line) => line.forEach((tri) => (tri.aab = [])));
    grid.forEach((line) => line.forEach((tri) => tri.setaab()));
  } // createGrid

  //------------------------------------------------------------------------
  function createBlock(tri, side) {
    if (tri.block) return false; // already belongs to a block : forget
    const initTri = tri;
    let nextTri;
    const block = [];
    while (true) {
      block.push(tri);
      tri.entry = side;
      tri.exit = tri.aab.findIndex((v, k) => k != tri.entry && v == 0);
      tri.block = block;
      /* fetch next triangle - if any */
      nextTri = tri.neighbors[tri.exit];
      if (nextTri === false) {
        block.closed = false;
        return block; // finished open path
      }
      if (nextTri === initTri) {
        block.closed = true;
        return block; // finished closed
      }
      side = 2 - tri.exit;
      tri = nextTri;
    } // while
  } // createBlock

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

  function createBlocks() {
    blocks = [];
    let block;
    // create all opened paths (begining on edge)
    grid.forEach((line) =>
      line.forEach((tri) => {
        if (tri.block) return; // already belongs to a block
        let ext = [];
        tri.neighbors.forEach((v, k) => {
          if (v === false && tri.aab[k] == 0) ext.push(k);
        });
        if (ext.length == 0) return; // not on external side, ignore
        block = createBlock(tri, ext[intAlea(ext.length)]);
        if (block) blocks.push(block);
      })
    );
    // create all closed paths
    grid.forEach((line) =>
      line.forEach((tri) => {
        if (tri.block) return; // already belongs to a block
        block = createBlock(
          tri,
          tri.aab.findIndex((v) => v === 0)
        );
        if (block) blocks.push(block);
      })
    );
    blocks.forEach(orientBlock);
    blocks.forEach((block) => (block.hue = intAlea(360)));
  }
  //------------------------------------------------------------------------
  function orientBlock(block) {
    block.forEach((tri, k) => {
      if ((tri.entry + 1) % 3 == tri.base) {
        tri.a = (tri.entry + 1) % 3; // base point of entry
        tri.b = tri.entry; // summit
        tri.c = (tri.entry + 2) % 3; // base point of exit
      } else {
        tri.a = tri.entry; // base point of entry
        tri.b = (tri.entry + 1) % 3; // summit
        tri.c = (tri.entry + 2) % 3; // base point of exit
      }
      let ntri = block[(k + 1) % block.length];
      tri.invertAlpha = ntri.dirBase == tri.dirBase;
    }); // block
  } // orientBlock
  //------------------------------------------------------------------------
  function drawBlock(block, alpha, hue, light, widthCoeff = 1) {
    const tri = block[0];
    let pint0;
    let pa = lerp(tri.vertices[tri.a], tri.vertices[tri.b], alpha);
    let pts = [pa];
    let pint;
    block.forEach((tri) => {
      pts.push(lerp(tri.vertices[tri.c], tri.vertices[tri.b], alpha));
      if (tri.invertAlpha) alpha = 1 - alpha;
    });

    ctx.beginPath();
    pts.forEach((p, k) => {
      if (k == 0) {
        pint0 = pint = lerp(p, pts[1], 0.5);
        if (block.closed) {
          ctx.moveTo(pint.x, pint.y);
        } else {
          ctx.moveTo(pa.x, pa.y);
          ctx.lineTo(pint.x, pint.y);
        }
      } else if (k == pts.length - 1) {
        if (block.closed) {
          ctx.quadraticCurveTo(pa.x, pa.y, pint0.x, pint0.y);
          ctx.closePath();
        } else ctx.lineTo(p.x, p.y);
      } else {
        pint = lerp(p, pts[k + 1], 0.5);
        ctx.quadraticCurveTo(p.x, p.y, pint.x, pint.y);
      }
    });
    const lw = 3 + (triHeight / nbLines - 3) * widthCoeff; // (width 3 for coeff 0)
    for (let l = lw; l > 0; --l) {
      ctx.lineWidth = l;
      ctx.strokeStyle = `hsl(${hue},100%,${20 + (light - 20) * (1 - l / lw)}%)`;
      ctx.stroke();
    }
  } // drawBlock
  //------------------------------------------------------------------------
  //------------------------------------------------------------------------
  function drawBlockRounded(block, alpha, hue, light, round) {
    /* similiar to drawBlock, the round parameter determines how corners are rounded, from 0(angular) to 1 (totally rounded)
        and no glossy effect is applied
        */
    const tri = block[0];
    let pint0;
    let pa = lerp(tri.vertices[tri.a], tri.vertices[tri.b], alpha);
    let pts = [pa];
    let pinta, pintb;
    block.forEach((tri) => {
      pts.push(lerp(tri.vertices[tri.c], tri.vertices[tri.b], alpha));
      if (tri.invertAlpha) alpha = 1 - alpha;
    });

    ctx.beginPath();
    pts.forEach((p, k) => {
      if (k == 0) {
        pint0 = pinta = lerp(p, pts[1], 0.5 * round);
        pintb = lerp(p, pts[1], 1 - 0.5 * round);
        if (block.closed) {
          ctx.moveTo(pinta.x, pinta.y);
        } else {
          ctx.moveTo(pa.x, pa.y);
        }
        ctx.lineTo(pintb.x, pintb.y);
      } else if (k == pts.length - 1) {
        if (block.closed) {
          ctx.quadraticCurveTo(pa.x, pa.y, pint0.x, pint0.y);
          ctx.closePath();
        } else ctx.lineTo(p.x, p.y);
      } else {
        pinta = lerp(p, pts[k + 1], 0.5 * round);
        pintb = lerp(p, pts[k + 1], 1 - 0.5 * round);
        ctx.quadraticCurveTo(p.x, p.y, pinta.x, pinta.y);
        ctx.lineTo(pintb.x, pintb.y);
      }
    });
    ctx.lineWidth = 3;
    ctx.strokeStyle = `hsl(${hue},100%,${light}%)`;
    ctx.stroke();
  } // drawBlockRounded

  //------------------------------------------------------------------------
  function* animateTri1() {
    const drawn = []; // already drawn triangles

    let tInit,
      dt,
      tri,
      current,
      rot,
      cCurr,
      cTarget,
      angle,
      duration,
      speed,
      kx,
      ky;
    txtexplain.innerHTML = "";
    addP("Tile a canvas with equilateral triangles.");
    addP(
      "These triangles must have 2 green edges and one red, and colors must match. It's a bit tricky."
    );
    ky = 0;
    kx = 0;
    for (let nb = 1; ; ++nb) {
      // let's animate tiles
      speed = mmin(nb * 0.3,3);
      //if (speed > 5) break;
      tInit = performance.now();
      // pick triangle
      tri = grid[ky][kx];
      cTarget = {};
      cTarget.x = tri.vertices.reduce((s, p) => s + p.x, 0) / 3;
      cTarget.y = tri.vertices.reduce((s, p) => s + p.y, 0) / 3;
      // create a fake triangle (for initial position and animation)
      current = new Triangle(hnbx, hnby);
      current.upsideDown = 0;
      current.setXY();
      current.aab = [0, 1, 0];
      cCurr = {};
      cCurr.x = current.vertices.reduce((s, p) => s + p.x, 0) / 3;
      cCurr.y = current.vertices.reduce((s, p) => s + p.y, 0) / 3;
      duration = mhypot(cTarget.x - cCurr.x, cTarget.y - cCurr.y) / speed;
      // rotation required to go from current to tri
      rot =
        ([
          [-2, 0, 2],
          [-1, 3, 1]
        ][tri.upsideDown][tri.base] *
          mPI) /
        3;
      while (true) {
        dt = mmin(duration, performance.now() - tInit);
        angle = (rot * dt) / duration - mPI / 2;
        let c = lerp(cCurr, cTarget, dt / duration);
        current.vertices[0].x = c.x + (triWidth / 2) * mcos(angle);
        current.vertices[0].y = c.y + (triWidth / 2) * msin(angle);
        current.vertices[1].x =
          c.x + (triWidth / 2) * mcos(angle + (2 * mPI) / 3);
        current.vertices[1].y =
          c.y + (triWidth / 2) * msin(angle + (2 * mPI) / 3);
        current.vertices[2].x =
          c.x + (triWidth / 2) * mcos(angle - (2 * mPI) / 3);
        current.vertices[2].y =
          c.y + (triWidth / 2) * msin(angle - (2 * mPI) / 3);
        // clear screen and draw triangles : drawn and current
        ctx.clearRect(0, 0, maxx, maxy);
        drawn.forEach((tri) => tri.drawaab(2, 1));
        current.drawaab(3, 1);
        yield;
        if (dt == duration) break; // exit "while" when done
      } // while
      drawn.push(tri);
      // pick next triangle
      if (ky == 0 || kx + 1 >= grid[0].length) {
        ky = kx + ky + 1;
        kx = 0;
        while (ky >= grid.length) {
          // should not happen
          --ky;
          ++kx;
        }
        if (kx >= grid[0].length) break; // exit "for", no more triangles to put
      } else {
        --ky;
        ++kx;
      }
    }
    ctx.clearRect(0, 0, maxx, maxy);
    grid.forEach((line) => line.forEach((tri) => tri.drawaab(2, 1)));
  } // animateTri1

  //------------------------------------------------------------------------
  function* animateTri2() {
    txtexplain.innerHTML = "";
    addP("Add each triangle hatch lines parallel to the red edge.");
    addP(`Here we are using ${nbLines} hatch lines.`);

    ctx.clearRect(0, 0, maxx, maxy);
    grid.forEach((line) => line.forEach((tri) => tri.drawaab(2, 1)));

    let kx, ky, tri;
    ky = 0;
    kx = 0;
    let tInit = performance.now();
    for (let nb = 1; ; ++nb) {
      // let's animate a few tiles
      tri = grid[ky][kx];
      for (let k = 0; k < nbLines; ++k) {
        tri.hatchaab((k + 0.5) / nbLines, 48, 2);
        yield;
      } // for k
      // pick next triangle
      if (ky == 0 || kx + 1 >= grid[0].length) {
        ky = kx + ky + 1;
        kx = 0;
        while (ky >= grid.length) {
          --ky;
          ++kx;
        }
        if (kx >= grid[0].length) break; // done all , exit for
      } else {
        --ky;
        ++kx;
      }
    } // for nb;

    ctx.clearRect(0, 0, maxx, maxy);
    grid.forEach((line) => line.forEach((tri) => tri.drawaab(2, 1)));
    grid.forEach((line) =>
      line.forEach((tri) => {
        for (let k = 0; k < nbLines; ++k)
          tri.hatchaab((k + 0.5) / nbLines, 48, 2);
      })
    );
  } // animateTri2

  //------------------------------------------------------------------------
  function* animateTri3() {
    txtexplain.innerHTML = "";
    addP("Delete the triangles.");

    let tInit = performance.now();
    const duration = 3000;

    while (true) {
      let lum = mmax(0, 1 - (performance.now() - tInit) / duration);
      ctx.clearRect(0, 0, maxx, maxy);
      grid.forEach((line) => line.forEach((tri) => tri.drawaab(2, lum)));
      grid.forEach((line) =>
        line.forEach((tri) => {
          for (let k = 0; k < nbLines; ++k)
            tri.hatchaab((k + 0.5) / nbLines, 48, 2);
        })
      );
      yield;
      if (lum == 0) break;
    }
    ctx.clearRect(0, 0, maxx, maxy);
    grid.forEach((line) =>
      line.forEach((tri) => {
        for (let k = 0; k < nbLines; ++k)
          tri.hatchaab((k + 0.5) / nbLines, 48, 2);
      })
    );
  } // animateTri3

  //------------------------------------------------------------------------
  function* animateTri4() {
    txtexplain.innerHTML = "";
    addP(
      "Connect segments togetether to actually form broken lines, or polygons if lines are closed."
    );
    ctx.clearRect(0, 0, maxx, maxy);
    grid.forEach((line) =>
      line.forEach((tri) => {
        for (let k = 0; k < nbLines; ++k)
          tri.hatchaab((k + 0.5) / nbLines, 48, 1.5);
      })
    );

    let nblocks = arrayShuffle(blocks.slice()); // shuffled list of blocks
    while (nblocks.length) {
      let block = nblocks.pop();
      for (let kLine = 0; kLine < nbLines; ++kLine) {
        let alpha = (kLine + 0.5) / nbLines;
        let lum = 30 + 50 * (kLine / (nbLines - 1));
        for (let kTri = 0; kTri < block.length; ++kTri) {
          block[kTri].hatchaab(alpha, block.hue, 3, lum);
          yield;
          if (block[kTri].invertAlpha) alpha = 1 - alpha;
        } // for kTri
      } // for kLine
    } // while nblock.length
  } // animateTri4
  //------------------------------------------------------------------------
  function* animateTri5() {
    let tInit, round;

    const duration = 3000;
    txtexplain.innerHTML = "";
    addP("Soften all these sharp angles.");

    tInit = performance.now();

    do {
      round = mmin(1, (performance.now() - tInit) / duration);
      ctx.clearRect(0, 0, maxx, maxy);
      blocks.forEach((block) => {
        for (let k = 0; k < nbLines; ++k) {
          drawBlockRounded(
            block,
            (k + 0.5) / nbLines,
            block.hue,
            30 + 50 * (k / (nbLines - 1)),
            round
          );
        } // for k
      }); // blocks. forEach
      yield;
    } while (round < 1);
  } // animateTri5

  //------------------------------------------------------------------------
  function* animateTri6() {
    let tInit, widthk;

    const duration = 3000;
    txtexplain.innerHTML = "";
    addP("Add some fun.");
    butt3.classList.add("hidden");
    butt2.innerHTML = "end";

    tInit = performance.now();

    do {
      widthk = mmin(1, (performance.now() - tInit) / duration);
      ctx.clearRect(0, 0, maxx, maxy);
      blocks.forEach((block) => {
        for (let k = 0; k < nbLines; ++k) {
          drawBlock(
            block,
            (k + 0.5) / nbLines,
            block.hue,
            30 + 50 * (k / (nbLines - 1)),
            widthk
          );
        } // for k
      }); // blocks. forEach
      yield;
    } while (widthk < 1);
  } // animateTri6

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

  let animate;

  {
    // scope for animate

    let animState;
    let subAnim;

    animate = function (tStamp) {
      const event = events.pop();
      let nxt;

      window.requestAnimationFrame(animate);

      if (event && event.event == "reset") animState = 0;
      if (event && event.event == "click") animState = 0;
      if (event && event.event == "butt3") {
        firstRun = false;
        animState = 1;
      }

      switch (animState) {
        case 0:
          if (startOver()) {
            ++animState;
          }
          break;

        case 1:
          animState++;
          if (firstRun) animState = 10;
          else explain.classList.add("hidden");
          break;

        case 2:
          ctx.clearRect(0, 0, maxx, maxy);
          blocks.forEach((block) => {
            for (let k = 0; k < nbLines; ++k)
              drawBlock(
                block,
                (k + 0.5) / nbLines,
                block.hue,
                30 + 50 * (k / (nbLines - 1))
              );
          });
          ++animState;
          break;

        case 10:
          subAnim = animateTri1();
          ++animState;
        case 11:
          nxt = subAnim.next();
          if (nxt.done) ++animState;
        // break; no ! no break here
        case 12:
          if (event && event.event == "butt2") animState = 15;
          break;

        case 15:
          subAnim = animateTri2();
          ++animState;
        case 16:
          nxt = subAnim.next();
          if (nxt.done) ++animState;
        case 17:
          if (event && event.event == "butt2") animState = 20;
          break;

        case 20:
          subAnim = animateTri3();
          ++animState;
        case 21:
          nxt = subAnim.next();
          if (nxt.done) ++animState;
        case 22:
          if (event && event.event == "butt2") animState = 25;
          break;

        case 25:
          subAnim = animateTri4();
          ++animState;
        case 26:
          nxt = subAnim.next();
          if (nxt.done) ++animState;
        case 27:
          if (event && event.event == "butt2") animState = 30;
          break;

        case 30:
          subAnim = animateTri5();
          ++animState;
        case 31:
          nxt = subAnim.next();
          if (nxt.done) ++animState;
        case 32:
          if (event && event.event == "butt2") animState = 35;
          break;

        case 35:
          subAnim = animateTri6();
          firstRun = false;
          ++animState;
        case 36:
          nxt = subAnim.next();
          if (nxt.done) ++animState;
        case 37:
          if (event && event.event == "butt2") animState = 1;
          break;
      } // switch
    }; // animate
  } // scope for animate

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

  function startOver() {
    // canvas dimensions

    maxx = window.innerWidth;
    maxy = window.innerHeight;

    canv.width = maxx;
    canv.height = maxy;
    ctx.lineJoin = "bevel";
    lRef = msqrt(maxx * maxy);
    triWidth = (lRef / 12) * alea(0.8, 1.2);
    triHeight = triWidth * rac3s2;

    hnby = mceil((maxy / triHeight - 1) / 2); // the full array has 2 * hnbx + 1 rows
    hnbx = mceil(maxx / triWidth);

    // uncomment the next 2 lines to see what's going on the edges
    //                hnbx -= 2;
    //                hnby -= 1;

    nbx = 1 + 2 * hnbx;
    nby = 1 + 2 * hnby;

    if (nbx < 3 || nby < 3) return false;
    nbLines = intAlea(2, 6);
    if (firstRun) {
      nbLines = 3;
    }
    ctx.clearRect(0, 0, maxx, maxy);
    createGrid();
    createBlocks();

    return true;
  } // startOver

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

  function mouseClick(event) {
    events.push({ event: "click" });
  } // mouseMove

  //------------------------------------------------------------------------
  //------------------------------------------------------------------------
  // beginning of execution

  {
    //    document.body.style.backgroundColor = bgColor;
    canv = document.createElement("canvas");
    canv.style.position = "absolute";
    document.body.appendChild(canv);
    ctx = canv.getContext("2d");
  } // création CANVAS
  canv.addEventListener("click", mouseClick); // just for initial position
  const explain = document.getElementById("explain");
  const txtexplain = document.getElementById("txtexplain");
  const butt2 = document.getElementById("butt2");
  butt2.addEventListener("click", () => events.push({ event: "butt2" }));
  const butt3 = document.getElementById("butt3");
  butt3.addEventListener("click", () => events.push({ event: "butt3" }));

  events = [{ event: "reset" }];
  requestAnimationFrame(animate);
}); // window load listener

External Link for Animated Triangles on Canvas JS HTML

Related Tags

No Items Found.

Add Comment
Type in a Nick Name here
 
Related Search Terms
Search Code
Search Code by entering your search text above.
Welcome to CSSBundle.com

Hi there and welcome to CSSBundle. Here at CSS Bundle we aim to find the best css and javascript bundles and demos and add them here to make them easy to find all in one place.

If you would like to stay up to date with our latest updates you can subscribe to our mailing list where we will send you one email per week with the latest updates, no spam emails just the latest updates.

Random Quote


Latest Code
css animations Falling Stars CSS Animation html Video different sources on screen sizes video embed html responsive css animations Animated Triangles on Canvas JS HTML HTML Canvas Instant colour fill with HTML Canvas javascript Transform Text into Images using SnapText JS CSS Tools Fancy Border Radius CSS Generator Tool - spin animation keyframes CSS Tools CSS Drop Filter Shadow Creator CSS Slider A CSS and HTML Only Carousel Slider CSS Z-Index Z-Index Code Front - CSS to change z-index when element is active sections section with image to the right text to the left gradient Sections Two Clickable Logo Promo Box Section Foundation HTML CSS Sections Inline Contact Form Formatting HTML CSS Sections Overview full width section in Poppins font with light gray background using foundation Interactive Images Map Image with Easy Dots and Titles using only CSS HTML Sections Social Header Links easy Drop in Code with Font Awesome Icons Sections Responsive Friendly Subscribe to our Newsletter Section CSS Tips Create a button with CSS, HTML and Center it! CSS Text Formatting Truncate Text to an amount of lines using line-clamp CSS Positions Item Positioning in CSS CSS Tips Draw a Circle in CSS css animations Magical Text Effect Bootstrap 4.2 Kitchen Sink CSS Frameworks Make your site look like windows 7 CSS Backgrounds Fancy up your banner image hero sections using overlay gradients CSS Modal Floating Modal Message Fixed on the Bottom of the Screen CSS Modal Create a modal with only CSS css grid display items in a css grid CSS Text Effects Using the selection selector to change the default highlight color Text Truncate your Text with CSS CSS Cursors Change your cursor into an Emoji Cursor or Image Cursor CSS How to Center Everything or Anything with CSS CSS Animations Smooth Scrolling with just CSS CSS Animations Radial Glow under cursor on box hover over effect CSS Shadows Using a filter drop shadow for transparent images CSS Animations Pure CSS 3D Flipping Book with Animations CSS Create a checkerboard background pattern with CSS CSS Animations button animations hover and fill animation CSS Animations button on focus swing animation CSS Animations Animated Button Border when Active Quick CSS create a border with top triangle in css CSS Animations Single Element CSS Spinners CSS Animations CSS Shake - Shake up your elements CSS Framework NES-style(8bit-like) CSS Framework CSS Tips Why to use a CSS Reset? Web Developer Checklists Front-End Checklist Icons Font Awesome Icons CSS Animation Animate Stuff with Animate.css CSS Animations Check Wave Click a Checkbox and Watch the Animation Animation Cloudy spiral animation with CSS3 Notifications Toast Messages and Notifications Standalone Library no JQuery - notify.js