simplex-noise实现canvas闪电交互动画效果代码

代码语言:html

所属分类:动画

代码描述:simplex-noise实现canvas闪电交互动画效果代码

代码标签: simplex-noise canvas 闪电 交互 动画

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

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

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

  
  
<style>
body {
    font-family: Helvetica sans-serif;
    padding: 0;
    margin: 0;
    background-color: #222;
    overflow: hidden;
    -webkit-user-select: none;
       -moz-user-select: none;
         -o-user-select: none;
        -ms-user-select: none;
            user-select: none;
}

canvas {
    position: absolute;
    top: 0;
    left: 0;
}
</style>

  
  
</head>

<body >
  <canvas id='c'></canvas>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/simplex-noise.js"></script>
      <script>
/**
 * requestAnimationFrame
 */
window.requestAnimationFrame = function () {
  return window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.oRequestAnimationFrame ||
  window.msRequestAnimationFrame ||
  function (callback) {window.setTimeout(callback, 1000 / 60);};
}();


/**
 * Lightning
 */
var Lightning = function (window) {

  /**
   * LightningAbstract
   */
  var LightningAbstract = {
    points: null,
    children: null,
    _simplexNoise: new SimplexNoise(),

    render: function (ctx, controls) {
      this._update(controls);
      this._draw(ctx);
    },

    _update: function (controls) {
      throw new Error('Not override');
    },

    _draw: function (ctx) {
      var points = this.points,
      isRoot = false,opts,
      p,p1,dx,dy,dist,
      lineWidth,
      i,len;

      isRoot = !this.parent;
      opts = isRoot ? this : this.parent;

      if (isRoot) {// is root
        var radius,gradient,
        children = this.children,c;

        ctx.save();
        for (i = 0, len = points.length; i < len; i += len - 1) {
          p = points[i];
          radius = Math.random() * (8 - 3) + 3;
          gradient = ctx.createRadialGradient(p.x, p.y, radius / 3, p.x, p.y, radius);
          gradient.addColorStop(0, this._colorToString(1));
          gradient.addColorStop(1, this._colorToString(0));
          ctx.fillStyle = gradient;
          ctx.beginPath();
          ctx.arc(p.x, p.y, radius, 0, Math.PI * 2, false);
          ctx.fill();
        }
        ctx.restore();

        for (i = 0, len = children.length; i < len; i += len - 1) {
          children[i].render(ctx);
        }
      }

      ctx.save();
      ctx.globalCompositeOperation = 'lighter';
      ctx.lineCap = 'round';
      ctx.fillStyle = 'rgba(0, 0, 0, 1)';
      ctx.shadowBlur = opts.blur;
      ctx.shadowColor = this._colorToString(1);
      ctx.beginPath();
      for (i = 0, len = points.length; i < len; i++) {
        p = points[i];
        if (len > 1) {
          p1 = points[i === len - 1 ? i - 1 : i + 1];
          dx = p.x - p1.x;
          dy = p.y - p1.y;
          dist = Math.sqrt(dx * dx + dy * dy);
        } else {
          dist = 0;
        }
        if (dist > 30) dist = 30;
        ctx.moveTo(p.x + dist, p.y);
        ctx.arc(p.x, p.y, dist, 0, Math.PI * 2, false);
      }
      ctx.fill();
      ctx.restore();

      ctx.save();
      ctx.beginPath();
      ctx.strokeStyle = this._colorToString(Math.random() * (opts.maxAlpha - opts.minAlpha) + opts.minAlpha);
      lineWidth = Math.random() * (opts.maxLineWidth - opts.minLineWidth) + opts.minLineWidth;
      ctx.lineWidth = isRoot ? lineWidth : lineWidth * 0.5;
      for (i = 0; i < len; i++) {
        p = points[i];
        ctx[i === 0 ? 'moveTo' : 'lineTo'](p.x, p.y);
      }
      ctx.stroke();
      ctx.restore();
    },

    _noise2d: function (x, y) {
      var octaves = 3,
      fallout = 0.5,
      amp = 1,f = 1,sum = 0,
      i;

      for (i = 0; i < octaves; ++i) {
        amp *= fallout;
        sum += amp * (this._simplexNoise.noise2D(x * f, y * f) + 1) * 0.5;
        f *= 2;
      }

      return sum;
    },

    _filterApply: function (points, lineLength, segmentsNum, base, amp, offset) {
      var pointsOld = this.points,
      // spline
      spline = [],
      catmullRom = this._catmullRom,
      p0,p1,p2,p3,t,per,
      // noise
      p,next,angle,sin,cos,nx,av,ax,ay,bv,bx,by,m,px,py,
      // shortest
      shortest,dx,dy,distSq,minDist,
      i,len,j,k;


      // Spline

      // スプライン補完用に配列の前後にラインの始点, 終点の参照をそれぞれ複製する
      points.unshift(points[0]);
      points.push(points[points.length - 1]);

      per = 1 / segmentsNum;

      // スプライン曲線のポイントを取得
      for (i = 0, len = points.length - 3; i < len; i++) {
        p0 = points[i];
        p1 = points[i + 1];
        p2 = points[i + 2];
        p3 = points[i + 3];

        for (j = 0; j < segmentsNum; j++) {
          t = (j + 1) * per;

          spline.push({
            x: catmullRom(p0.x, p1.x, p2.x, p3.x, t),
            y: catmullRom(p0.y, p1.y, p2.y, p3.y, t) });

        }
      }

      // 補完用に追加した参照を削除
      points.pop();
      // 削除のついでに描画の始点として追加
      spline.unshift(points.shift());


      // Noise

      points = [];
      len = spline.length;
      per = 1 / (len - 1);
      base = 1 / base;

      for (i = 0, len = spline.length; i < len; i++) {
        p = spline[i];
        next = i === len - 1 ? p : spline[i + 1];

        angle = Math.atan2(next.y - p.y, next.x - p.x);
        sin = Math.sin(angle);
        cos = Math.cos(angle);

        nx = i * base;

        av = lineLength * this._noise2d(nx - offset, offset) * 0.5 * amp;
        ax = av * sin;
        ay = av * cos;

        bv = lineLength * this._noise2d(nx + offset, offset) * 0.5 * amp;
        bx = bv * sin;
        by = bv * cos;

        m = Math.sin(Math.PI * (i * per));

        px = p.x + (ax - bx) * m;
        py = p.y - (ay - by) * m;

        if (pointsOld.length) {
          p = pointsOld.shift();
          p.x = px;
          p.y = py;
        } else {
          p = { x: px, y: py };
        }

        points.push(p);
      }


      // Shortest

      shortest = [points[0]];

      for (i = 0, len = points.length; i < len; i++) {
        p = points[i];

        minDist = Infinity;
        k = -1;
        for (j = i; j < len; j++) {
          p2 = points[j];

          dx = p.x - p2.x;
          dy = p.y - p2.y;
          distSq = dx * dx + dy * dy;

          if (p !== p2 && distSq < minDist * minDist) {
            minDist = Math.sqrt(distSq);
            k = j;
          }
        }
        if (k < 0) break;

        shortest.push(points[k]);
        i = k - 1;
      }

      return shortest;
    },

    _catmullRom: function (p0, p1, p2, p3, t) {
      var v0 = (p2 - p0) * 0.5,
      v1 = (p3 - p1) * 0.5;
      return (2 * p1 - 2 * p2 + v0 + v1) * t * t * t +
      (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t * t + v0 * t + p1;
    },

    _colorToString: function (alpha) {
      var c = this.color;
      return this.colorType === 'rgb' ?
      'rgba(' + c.join(',') + ',' + alpha + ')' :
      'hsla(' + c[0] + ',' + c[1] + '%,' + c[2] + '%,' + alpha + ')';
    } };



  /**
   * @constructor
   */
  function Lightning(segmentsNum) {
    this.points = [];
    this.children = [];

    this._params = [];
    this._offsets = [];
  }

  Lightning.prototype = extend(LightningAbstract, {
    color: [255, 255, 255],
    colorType: 'rgb',
    blur: 50,
    maxAlpha: 1,
    minAlpha: 0.75,
    maxLineWidth: 5,
    minLineWidth: 0.5,
    _params: null,
    _offsets: null,

    addParam: function (segmentsNum, base, amplitude, speed) {
      this._params.push({
        segmentsNum: segmentsNum,
        base: base,
        amplitude: amplitude,
        speed: speed });

      this._offsets[this._params.length - 1] = 0;
    },

    createChild: function (base, amplitude, speed) {
      var child = new LChild(this, {
        base: base || this._params.base,
        amplitude: amplitude || this._params.amplitude,
        speed: speed || this._params.speed });

      this.children.push(child);
      return child;
    },

    _update: function (points) {
      var params = this._params,param,
      offsets = this._offsets,
      lineLength,
      p0,p1,dx,dy,
      i,ilen,j,jlen;

      for (i = 0, ilen = params.length; i < ilen; i++) {
        param = params[i];

        lineLength = 0;
        for (j = 0, jlen = points.length; j < jlen; j++) {
          if (j !== jlen - 1) {
            p0 = points[j];
            p1 = points[j + 1];
            dx = p0.x - p1.x;
            dy = p0.y - p1.y;
            lineLength += dx * dx + dy * dy;
          }
        }
        lineLength = Math.sqrt(lineLength);

        offsets[i] += Math.random() * param.speed;

        points = this._filterApply(points, lineLength, param.segmentsNum, param.base, param.amplitude, offsets[i]);
      }

      this.points = points;
    } });



  /**
   * LChild
   */
  function LChild(parent, param) {
    this.parent = parent;

    this.points = [];
    this._param = param;
  }

  LChild.prototype = extend(LightningAbstract, {
    parent: null,
    _startStep: 0,
    _endStep: 0,
    _separate: 2,
    _param: null,
    _offset: 0,
    _lastChangeTime: 0,

    _update: function () {
      var parent = this.parent,
      plen = this.parent.points.length,
      param = this._param,
      points = [],
      currentTime,
      range,rangeLen,sep,seg,
      c0,c1,dx,dy,lineLength,
      i,j;

      // 一定時間ごと, あるいは親のポイントの数が子の終了ステップ位置を下回った場合に始点と終点の親からの取得位置を更新する
      currentTime = new Date().getTime();
      if (
      currentTime - this._lastChangeTime > 10000 * Math.random() ||
      plen < this._endStep)
      {
        var stepMin = plen * 0.1 | 0,
        startStep = this._startStep = Math.random() * (plen / 3 * 2 | 0) | 0;
        this._endStep = start.........完整代码请登录后点击上方下载按钮下载查看

网友评论0