canvas彩色石头动画效果代码

代码语言:html

所属分类:动画

代码描述:canvas彩色石头动画效果代码

代码标签: canvas 彩色 石头 动画

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

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  

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



  
  
</head>

<body>
  
  
      <script >
"use strict";

const DIST_BRIDSON_MIN = 0.08; // size of pattern - relative to canvas size
const DIST_BRIDSON_MAX = 0.15;

let canv, ctx; // canvas and context
let maxx, maxy; // canvas dimensions
let tr, distBridson;
let polygons;
let gr, shiftAlpha;

// for animation
let messages;
let colorMode = 0;

// 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 mPIS3 = Math.PI / 3;
const m2PI = Math.PI * 2;
const m2PIS3 = (Math.PI * 2) / 3;
const msin = Math.sin;
const mcos = Math.cos;
const matan2 = Math.atan2;

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

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

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
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function lerp(p0, p1, alpha) {
  return {
    x: (1 - alpha) * p0.x + alpha * p1.x,
    y: (1 - alpha) * p0.y + alpha * p1.y
  };
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------

function generatePoints(t) {
  return t.points.map((p) => ({ x: p.x, y: p.y })); // shallow copy
} // generate points

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

class SortedArray {
  /* creates a sorted array of any kind of things, by intersting them into an initially empty array
         the comparison function used for sorting is given to the constructor
         Things are inserted using .insert method
         sorted array is in the .tb property of the instance
      */
  /*
      the indexOf property lets you know if thing already existed according to fCompar, and at what index
      just use doInsert(no parameters) after indexOf to insert thing at found (not -1) index
      */

  /* CAUTION : if duplicates are allowed, indexOf is NOT garanteed to return the index of actual thing - only a thing
            which returns 0 when compared with given thing. Use regular Array.indexOf on instance.tb instead.
      */
  constructor(fCompar, keepDuplicates = false) {
    /* fCompar is the function which will be called to compare the things that will be inserted
        in  this.tb
            fCompar(a,b) must return  number < 0 if a must be placed before b
            == 0 if a and b are considered equal
            > 0 if a must be placed after b
        */
    this.tb = [];
    this.fCompar = fCompar;
    this.keepDuplicates = keepDuplicates;
  }

  indexOf(thing) {
    this.thing = thing;
    // search for place to insert thing, using comparison function this.fCompar
    // if found, returns the index of thing in this.tb, else returns -1
    // sets this.insertAt for future insertion

    let cmp;
    if (this.tb.length == 0) {
      this.insertAt = 0;
      return -1;
    }
    let a = 0,
      c = this.tb.length - 1;
    let b;

    do {
      b = Math.floor((a + c) / 2);
      cmp = this.fCompar(this.tb[b], thing);
      switch (true) {
        case cmp < 0: // thing after b
          if (b == c) {
            // beyond c
            this.insertAt = c + 1;
            return -1;
          }
          if (a == b) ++b; // not to stay on same interval, if its length is 1 or 2
          a = b; // after b : search (b, c) now
          break;
        case cmp == 0:
          this.insertAt = b;
          return b; // found !

        default:
          //thing before b
          if (b == a) {
            // before a
            this.insertAt = a;
            return -1;
          }
          c = b; // search (a, b) now
      }
    } while (true);
  } // indexOf

  doInsert() {
    // DO NOT CALL TWICE WITHOUT getting new (!= -1) indexOf
    this.tb.splice(this.insertAt, 0, this.thing);
  }

  insert(thing) {
    /* inserts thing */
    if (this.indexOf(thing) != -1 && !this.keepDuplicates) return; // already exists and refused
    this.tb.splice(this.insertAt, 0, thing);
  }
} // class SortedArray
//------------------------------------------------------------------------
class Edge {
  constructor(p0, p1) {
    if (p0.kp <= p1.kp) {
      this.p0 = p0;
      this.p1 = p1;
    } else {
      this.p0 = p1;
      this.p1 = p0;
    }
    this.tris = []; // to record up to 2 triangles attached to this edge
  }

  attachTriangle(tri) {
    // includes a triangle in and edge's tris array
    // AND includes itself in this triangle's edges array
    // AND more

    if (!this.p0.tris.includes(tri)) this.p0.tris.push(tri);
    if (!this.p1.tris.includes(tri)) this.p1.tris.push(tri);

    if (!this.p0.edges.includes(this)) this.p0.edges.push(this);
    if (!this.p1.edges.includes(this)) this.p1.edges.push(this);

    if (tri.a == this.p0) {
      if (tri.b == this.p1) {
        this.tris[0] = tri;
        tri.edges[0] = this;
      } else {
        this.tris[1] = tri;
        tri.edges[2] = this;
      }
      return;
    }
    if (tri.b == this.p0) {
      if (tri.c == this.p1) {
        this.tris[0] = tri;
        tri.edges[1] = this;
      } else {
        this.tris[1] = tri;
        tri.edges[0] = this;
      }
      return;
    }
    if (tri.c == this.p0) {
      if (tri.a == this.p1) {
        this.tris[0] = tri;
        tri.edges[2] = this;
      } else {
        this.tris[1] = tri;
        tri.edges[1] = this;
      }
    }
  }
} // class Edge
//------------------------------------------------------------------------

class Triangle {
  constructor(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
    this.vertices = [this.a, this.b, this.c];

    const m11 = 2 * (b.x - a.x);
    const m21 = 2 * (c.x - a.x);
    const m12 = 2 * (b.y - a.y);
    const m22 = 2 * (c.y - a.y);
    const c1 = b.x * b.x - a.x * a.x + b.y * b.y - a.y * a.y;
    const c2 = c.x * c.x - a.x * a.x + c.y * c.y - a.y * a.y;
    const det = m11 * m22 - m21 * m12;
    this.xc = (c1 * m22 - c2 * m12) / det;
    this.yc = (m11 * c2 - m21 * c1) / det;
    this.r = Math.hypot(this.xc - this.a.x, this.yc - this.a.y);
  } // constructor

  inCircumCircle(p) {
    return Math.hypot(p.x - this.xc, p.y - this.yc) < this.r;
  }
  hasEdge(p1, p2) {
    // (was written before the above "Edge" class)
    return (
      (p1 == this.a || p1 == this.b || p1 == this.c) &&
      (p2 == this.a || p2 == this.b || p2 == this.c)
    );
  }
  listTris() {
    let other;
    this.tris = [];
    this.edges.forEach((edge, kEdge) => {
      other = edge.tris[0] == this ? edge.tris[1] : edge.tris[0];
      if (other) this.tris[kEdge] = other;
    });
  } // listTris

  draw(fillStyle, lineWidth, strokeStyle) {
    ctx.beginPath();
    ctx.moveTo(this.a.x, this.a.y);
    ctx.lineTo(this.b.x, this.b.y);
    ctx.lineTo(this.c.x, this.c.y);
    ctx.closePath();
    if (fillStyle) {
      ctx.fillStyle = fillStyle;
      ctx.fill();
    }
    if (lineWidth) {
      ctx.lineWidth = lineWidth;
      ctx.strokeStyle = strokeStyle;
      ctx.stroke();
    }
  } // draw
} // Triangle

//------------------------------------------------------------------------
class Delaunay {
  /* Delaunay based on based on https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm
   */
  constructor(points, maxx, maxy, margin) {
    let triangulation, badTriangles, polygon;
    /* maxx and maxy are given here ONLY to determine the initial triangle, assuming
        all points are in a (-margin, -margin)-(maxx + margin, maxy + margin) rectangle
        actual extreme coordinates of points could be used instead of maxx and maxy
        maxx and maxy are both supposed > 0
        */

    /*   triangulation := empty triangle mesh data structure*/
    /* add super-triangle to triangulation // must be large enough to completely contain all the points in pointList */
    /* the super-triangle has a vertex in [-margin-1, -margin-1] and 2 sides parallel to the axis
        The 3rd side is a 45 degrees slanted line passing by [maxx + margin + 1, maxy + margin + 1]
        /!\ CAUTION : all triangles generated will have the same orientation as this initial super-triangle
        */
    const numPts = points.length;

    const pts = points.map((p, kp) => ({ x: p.x, y: p.y, kp })); // array of points - future vertices
    this.points = pts;
    let supert = [
      { x: -margin - 1, y: maxx + maxy + 3 * margin + 3 }, // points turning clockwise on a JS 2D canvas
      { x: -margin - 1, y: -margin - 1 },
      { x: maxx + maxy + 3 * margin + 3, y: -margin - 1 }
    ];
    triangulation = [new Triangle(...supert)];

    /*
           for each point in pointList do // add all the points one at a time to the triangulation
        */
    for (let kp = 0; kp < numPts; ++kp) {
      let point = pts[kp];

      /*
                badTriangles := empty set
          */
      badTriangles = [];
      /*
                for each triangle in triangulation do // first find all the triangles that are no longer valid due to the insertion
          */
      for (let kt = 0; kt < triangulation.length; ++kt) {
        if (triangulation[kt].inCircumCircle(point))
          badTriangles.push(triangulation[kt]);
      } // for kt

      polygon = [];
      for (let kt = 0; kt < badTriangles.length; ++kt) {
        let tri = badTriangles[kt];
        if (
          !badTriangles.some(
            (othertri) => othertri !== tri && othertri.hasEdge(tri.a, tri.b)
          )
        )
          polygon.push([tri.a, tri.b]);
        if (
          !badTriangles.some(
            (othertri) => othertri !== tri && othertri.hasEdge(tri.b, tri.c)
          )
        )
          polygon.push([tri.b, tri.c]);
        if (
          !badTriangles.some(
            (othertri) => othertri !== tri && othertri.hasEdge(tri.c, tri.a)
          )
        )
          polygon.push([tri.c, tri.a]);
      } // for kt

      /* remove bad triangles from triangulation */
      for (let kt = 0; kt < badTriangles.length; ++kt) {
        let tri = badTriangles[kt];
        triangulation.splice(triangulation.indexOf(tri), 1);
      } // for kt
      /* add triangulation new triangles built on point and polygon
       */
      polygon.forEach((edge) =>
        triangulation.push(new Triangle(point, edge[0], edge[1]))
      );
    } // points.forEach
    /* remove super-triangle */
    for (let kt = triangulation.length - 1; kt >= 0; --kt) {
      let tri = triangulation[kt];
      if (supert.includes(tri.a)) {
        triangulation.splice(kt, 1);
        continue;
      }
      if (supert.includes(tri.b)) {
        triangulation.splice(kt, 1);
        continue;
      }
      if (supert.includes(tri.c)) {
        triangulation.splice(kt, 1);
        continue;
      }
    }

    this.triangulation = triangulation;
  } // constructor

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

  analyze() {
    this.points.forEach((p) => {
      p.tris = [];
      p.edges = [];
    });
    this.triangulation.forEach((tri) => (tri.edges = []));

    this.edgesList = new SortedArray((e0, e1) => {
      if (e0.p0.kp - e1.p0.kp) return e0.p0.kp - e1.p0.kp;
      else return e0.p1.kp - e1.p1.kp;
    });

    this.triangulation.forEach((tri) => {
      let ed = new Edge(tri.a, tri.b);
      let kedge = this.edgesList.indexOf(ed);
      if (kedge == -1) this.edgesList.doInsert();
      else ed = this.edgesList.tb[kedge];
      ed.attachTriangle(tri);
      ed = new Edge(tri.b, tri.c);
      kedge = this.edgesList.indexOf(ed);
      if (kedge == -1) this.edgesList.doInsert();
      else ed = this.edgesList.tb[kedge];
      ed.attachTriangle(tri);
      ed = new Edge(tri.c, tri.a);
      kedge = this.edgesList.indexOf(ed);
      if (kedge == -1) this.edgesList.doInsert();
      else ed = this.edgesList.tb[kedge];
      ed.attachTriangle(tri);
    });

    /* sort triangles around every point */

    this.points.forEach((p) => {
      const newEdges = [];
      const newTris = [];
      let edge0, tri;

      if (p.tris.length != p.edges.length) {
        // if point is on edge of complete figure
        edge0 = p.edges.find(
          (edge) =>
            (edge.p0 == p && edge.tris[0] && !edge.tris[1]) ||
            (edge.p1 == p && edge.tris[1] && !edge.tris[0])
        );
        if (edge0 === undefined) edge0 = p.edges[0];
      } else {
        edge0 = p.edges[0];
      }
      while (true) {
        /* find triangle with vertex p and edge edge0 starting from p and turning clockwise */
        newEdges.push(edge0);
        tri = edge0.tris[edge0.p0 == p ? 0 : 1];
        if (tri === undefined) break; // p was on perimeter, reached end
        newTris.push(tri);
        if (newEdges.length == p.edges.length) break; // made full revolution around p
        /* find other side of tri ending at p */
        switch (p) {
          case tri.a:
            edge0 = tri.edges[2];
            break; // ca
          case tri.b:
            edge0 = tri.edges[0];
            break; // ab
          case tri.c:
            edge0 = tri.edges[1];
            break;
        } // switch
      } // while (true)
      p.tris = newTris;
      p.edges = newEdges;
    });
  } // analyze
  drawEdges() {
    ctx.lineWidth = 1.5;
    ctx.strokeStyle = "#ffffff80";
    this.edgesList.tb.forEach((edge) => {
      ctx.beginPath();
      ctx.moveTo(edge.p0.x, edge.p0.y);
      ctx.lineTo(edge.p1.x, edge.p1.y);
      ctx.stroke();
    });
  } // drawEdges
  //------------------------------------------------------------------------
  deleteSharps() {
    /* remove triangles with too sharp angles from the periphery of the triangulation */
    let sharps = [];
    let cosangle;
    let p0, p1, dx, dy, len;
    this.triangulation.forEach((tri) => {
      dx = [];
      dy = [];
      len = [];
      for (let k = 0; k < 3; ++k) {
        p0 = tri.vertices[k];
        p1 = tri.vertices[(k + 1) % 3];
        dx[k] = p1.x - p0.x;
        dy[k] = p1.y - p0.y;
        len[k] = mhypot(dx[k], dy[k]);
      }
      for (let k = 0; k < 3; ++k) {
        let k1 = (k + 1) % 3;
        cosangle = -(dx[k1] * dx[k] + dy[k] * dy[k1]) / len[k] / len[k1];
        if (cosangle > 0.9) {
          sharps.push(tri);
          break;
        } // very sharp angle
      }
    }); //

    while (sharps.length) {
      let found, kfound; // changed nothing (yet) during this loop
      kfound = sharps.findIndex(
        (tri) => tri.tris.filter(() => true).length < 3
      );
      if (kfound == -1) brea.........完整代码请登录后点击上方下载按钮下载查看

网友评论0