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;
  caret-color: transparent;
}

input[type=text] {
  caret-color: auto;
}

#menu {
  font-size: 80%;
  margin: 0;
  padding: 5px;
  position: absolute;
  left: 5px;
  top: 5px;
  border-radius: 10px;
  background-color: rgba(255, 255, 128, 0.9);
  color: black;
  z-index: 10;
}

#menu.hidden #showhide{
  display: none;
}

#controls {
  margin-top: 0px;
  margin-bottom: 0px;

}

#menu button {
  margin-right: 5px;
  margin-left: 5px;
  border-radius: 5px;
}

#menu .center {
  text-align: center;
}

#colorspan {
  width: 3em;
  margin: 0 5px;
  padding: 0 1em;
  border: 1px solid black;
}

#colorspan.colorful {
  background: linear-gradient(to right, orange 20%, green 21%, green 40%, fuchsia 41%, fuchsia 60%,yellow 61%, yellow 80%, blue 81%, blue);
}
</style>



</head>

<body >
  <div id ="menu">
<p id="controls">close controls</p>
<div id="showhide">
<hr>
<p>speed field :</p>
<p><input type="range" min=30 max=150 step="any" value=70 id="wavelength"> wavelength</p>
<p><input type="range" min=0 max=1 step="any" value=0.5 id="amplitude"> amplitude</p>
<hr>
<p>particles :</p>
<p><input type="range" min=0 max=400 step="any" value=180 id="hue"> color <span id=colorspan></span></p>
<p><input type="range" min=1 max=30 step="any" value=10 id="diam"> diameter</p>
<p><input type="checkbox" id="randomdiam"> random diameter</p>
<p><input type="checkbox" id="ftl" checked> from top left</p>
<p><input type="checkbox" id="fbr"> from bottom right</p>
<hr>
<p class=center><button id=pattern>Draw pattern</button></p>
<p id="patternstatus">In progress. Please wait</p>
<hr>
<p>click canvas to reset</p>
</div> <!-- showhide -->
</div> <!-- menu -->

  
      <script >
"use strict";

window.addEventListener("load", function () {

  const speed = 3; // intensity of field at the triangle vertices
  const nbParticles = 100;
  const lifeTime = 10000 / speed;

  let ui, uiv;

  let canv, ctx; // canvas and context
  let maxx, maxy; // canvas dimensions
  let nbx, nby; // number of triangles

  let triangles; // array of triangles
  let particles; // array of particles
  let fields;
  let messages;
  let triWidth, triHeight; // length of triangle side

  let lineColor;
  let nbx2, nby2, midx, midy;
  let nbAlive;
  let nbGen;

  // 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;

  const sinPIS6 = 0.5;
  const cosPIS6 = rac3s2;
  const sinPIS3 = cosPIS6;
  const cosPIS3 = sinPIS6;

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

  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
  }

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

  /* returns intermediate point between p0 and p1,
    alpha = 0 whill preturn p0, alpha = 1 will return p1
    values of alpha outside [0,1] may be used to compute points outside the p0-p1 segment
  */
  function intermediate(p0, p1, alpha) {

    return [(1 - alpha) * p0[0] + alpha * p1[0],
    (1 - alpha) * p0[1] + alpha * p1[1]];
  } // function intermediate

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

  function Noise1DOneShot(periodmin, periodmax, min = 0, max = 1, random) {
    /* returns a 1D single-shot noise generator.
       the (optional) random function must return a value between 0 and 1
      the returned function has no parameter, and will return a new number every tiime it is called.
      If the random function provides reproductible values (and is not used elsewhere), this
      one will return reproductible values too.
      period should be > 1. The bigger period is, the smoother output noise is
    */
    random = random || Math.random;
    let currx = random(); // start with random offset
    let y0 = min + (max - min) * random(); // 'previous' value
    let y1 = min + (max - min) * random(); // 'next' value
    let period = periodmin + (periodmax - periodmin) * random();
    let dx = 1 / period;

    return function () {
      currx += dx;
      if (currx > 1) {
        currx -= 1;
        period = periodmin + (periodmax - periodmin) * random();
        dx = 1 / period;
        y0 = y1;
        y1 = min + (max - min) * random();
      }
      let z = (3 - 2 * currx) * currx * currx;
      return z * y1 + (1 - z) * y0;
    };
  } // Noise1DOneShot

  //------------------------------------------------------------------------
  // User Interface (controls)
  //------------------------------------------------------------------------
  function toggleMenu() {
    if (menu.classList.contains("hidden")) {
      menu.classList.remove("hidden");
      this.innerHTML = "close controls";
    } else {
      menu.classList.add("hidden");
      this.innerHTML = "controls";
    }
  } // toggleMenu


  //------------------------------------------------------------------------
  function prepareUI() {

    // toggle menu handler

    document.querySelector("#controls").addEventListener("click", toggleMenu);

    ui = {}; // User Interface HTML elements
    uiv = {}; // User Interface values of controls

    ["wavelength", "amplitude", "hue", "diam", "randomdiam", "ftl", "fbr", "colorspan",
    "pattern", "patternstatus"].forEach(ctrlName => ui[ctrlName] = document.getElementById(ctrlName));

    registerControl("wavelength", readUIFloat, setUIValue, "change", changeWaveLength);
    registerControl("amplitude", readUIFloat, setUIValue, "input", createField);
    registerControl("hue", readUIFloat, setUIValue, "input", displayHue);
    registerControl("diam", readUIFloat, setUIValue, "input");
    registerControl("randomdiam", readUICheck, setUIChecked, "input");
    registerControl("ftl", readUICheck, setUIChecked, "input");
    registerControl("fbr", readUICheck, setUIChecked, "input");

    ui.pattern.addEventListener("click", () => {messages.push({ message: "pattern" });});
    readUI();
    toggleProgress();
  } // prepareUI

  //------------------------------------------------------------------------
  function readUI() {

    if (ui.registered) {
      for (const ctrl in ui.registered) ui.registered[ctrl].readF();
    }
    displayHue();
  } // readUI

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function registerControl(controlName, readFunction, setFunction, changeEvent, changedFunction) {
    /* provides simple way to associate controls with their read / update / changeEvent / changed functions
    since many (but not all) controls work almost the same way */
    /* changeEvent and changedFunction are optional */

    const ctrl = ui[controlName];
    ui.registered = ui.registered || [];
    ui.registered.push(ctrl); // NEVER register a control twice !!!
    ctrl.readF = readFunction;
    ctrl.setF = setFunction;
    ctrl.changedF = changedFunction;
    if (changeEvent) {
      ctrl.addEventListener(changeEvent, event => {
        readFunction.call(ctrl);
        if (changedFunction) changedFunction.call(ctrl, event);
      });
    }
  } // registerControl
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function readUIFloat() {
    uiv[this.id] = parseFloat(this.value);
  }
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function readUIInt() {
    uiv[this.id] = parseInt(this.value);
  }
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function readUICheck() {
    uiv[this.id] = this.checked;
  }
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function setUIValue(value) {
    this.value = value;
    if (this.readF) this.readF();
  }
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function setUIChecked(checked) {
    if (checked) this.setAttribute("checked", "");else this.removeAttribute("checked");
    if (this.readF) this.readF();
  }
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  function setUIControls(params) {
    /* caution ! changing wavelength deletes the picture */

    for (let ctrl in params) {
      if (ui[ctrl].setF) ui[ctrl].setF(params[ctrl]);
      if (ui[ctrl].changedF) ui[ctrl].changedF(params[ctrl]);
    }

  } // setUIControls

  //------------------------------------------------------------------------
  function displayHue() {
    if (uiv.hue < 360) {
      ui.colorspan.style.backgroundColor = `hsl(${uiv.hue}, 100%, 50%)`;
      ui.colorspan.classList.remove("colorful");
    } else {
      ui.colorspan.classList.add("colorful");
    }
  }
  //------------------------------------------------------------------------
  function changeWaveLength() {

    triWidth = uiv.wavelength;
    triHeight = triWidth * rac3s2;

    // number of rows / columns

    nbx = 2 * mfloor(maxx / triWidth) + 5;
    nby = mfloor((mfloor(maxy / triHeight) + 1) / 2) * 2 + 4;

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

    createFieldDir();
    createField();

  }
  //------------------------------------------------------------------------
  // class Triangle

  function Triangle(kx, ky) {

    nbx2 = nbx >>> 1;
    nby2 = nby >>> 1;

    this.kx = kx;
    this.ky = ky;
    this.upsideDown = !!(kx + ky + nbx2 + nby2 & 1); // points up(false) or down(true)
    this.vertices = [[], [], []];
    // center of triangle (in fact, middle of height)
    let ya = midy + (ky - nby2) * triHeight;
    let xa = midx + (kx - nbx2) * triWidth / 2;

    // vertex 0
    this.vertices[0][0] = xa;
    this.vertices[0][1] = ya + triHeight / 2 * (this.upsideDown ? 1 : -1);

    // vertex 1
    this.vertices[1][0] = xa + triWidth / 2;
    this.vertices[1][1] = ya + triHeight / 2 * (this.upsideDown ? -1 : 1);

    // vertex 2
    this.vertices[2][0] = xa - triWidth / 2;
    this.vertices[2][1] = ya + triHeight / 2 * (this.upsideDown ? -1 : 1);

  } // function Triangle

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Triangle.prototype.projections = function (x, y) {

    /* the given point is supposed to be inside the triangle
    computes the coordinates of the projection of this point on 3 sides */

    let proj = [[], [], []];
    proj[0] = [x, this.vertices[1][1]];

    if (this.upsideDown) {
      proj[2][0] = (3 * triWidth / 2 + x + 3 * this.vertices[0][0] + (this.vertices[1][1] - y) * rac3) / 4;
      proj[2][1] = ((this.vertices[0][0] - x) * rac3 + 3 * y + this.vertices[1][1] + rac3s2 * triWidth) / 4;

      proj[1][0] = (x + 3 * this.vertices[0][0] + rac3 * (y - this.vertices[1][1]) - 1.5 * triWidth) / 4;
      proj[1][1] = ((x - this.vertices[0][0]) * rac3 + 3 * y + this.vertices[1][1] + rac3s2 * triWidth) / 4;

    } else {
      proj[2][1] = (y * rac3 + this.vertices[0][1] / rac3 + x - this.vertices[0][0]) / (rac3 + 1 / rac3);
      proj[2][0] = rac3 * (y - proj[2][1]) + x;

      proj[1][1] = (y * rac3 + this.vertices[0][1] / rac3 - x + this.vertices[0][0]) / (rac3 + 1 / rac3);
      proj[1][0] = x - rac3 * (y - proj[1][1]);
    }
    return proj;

  }; //

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

  Triangle.prototype.draw =.........完整代码请登录后点击上方下载按钮下载查看

网友评论0