js实现旋转波纹效果

代码语言:html

所属分类:粒子

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

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

<title>Trochoid ribbons</title>
<style>
      * { box-sizing:border-box; }
  body { font-family:sans-serif; font-size:0.8em; background:#000; }
  canvas { max-width:100%; border-radius:400px; }
  #pmenu { border:none; cursor:pointer; padding:none; font-weight:bold; background:none; line-height:1; font-size:26pt; }
  div.mb { position:relative; margin:0; padding:0; text-align:center; font-weight:bold; border-top:1px dotted #BBB; border-right:1px solid transparent; border-bottom:1px solid transparent; border-left:1px solid transparent; }
  div.pholder { width:188px; padding:4px; height:25px; }
  div.slider { position:absolute; top:0px; left:0px; height:26px; opacity:0; background:hsl(240, 30%, 86%); display:block; width:70px; padding:4px 0; border-right:4px solid hsl(240,30%,74%); }
/* 70 of 132 */
  div.rtext { position:absolute; top:0px; left:0px; }
  div.rlabel { display:inline-block; float:left; white-space:nowrap; padding:4px 0; width:120px; }
  div.rep { display:inline-block; float:left; width:32px;margin-right:2px; border-left:1px dotted #EEE; padding:4px 4px 4px 0; text-align:right; }
  div.inputdiv { position:absolute; top:0px; left:0px; height:26px; border:1px solid transparent; }
  input.range { height:24px; opacity:0.01; cursor:pointer; margin:0; width:152px; }
  input.cb { position:absolute; top:0; left:0; opacity:0.01; width:100%; height:24px; cursor:pointer; margin:0; }
  div.infoc { border:1px dotted #99F; }
  div.locksym { text-align:center; font-size:14px; padding:2px 0; color:black; }
  div.lockdiv { position:relative; padding:0; float:right; height:24px; height:26px; margin-top:-1px; }

  </style>

</head>
<body translate="no">
<div style="text-align:center;"><canvas id="cta" width="800" height="800"></canvas></div>
<div class="bgfade" style="position:absolute; top:8px; right:8px;">
<div style="text-align:right;">
<button id="pmenu">
<span id="xmrep" style="opacity:0;">&#215;</span>
<span id="pmrep" style="position:absolute; top:3px; right:5px; color:white; opacity:.8;">&#8801;</span>
</button>
</div>
<div id="ctl" style="opacity:0;">
<div class="mb" style="width:100%; height:24px;">
<div class="pholder">&#160;</div>
<div class="rtext">
<div id="ss" class="rlabel" style="width:188px;">Stop</div>
</div>
<div class="inputdiv">
<button style="width:188px; height:24px; display:block; cursor:pointer; padding:0; border:none; opacity:.01;" onfocus="btnFocus(this)" onblur="btnBlur(this)" onmouseover="bmov(this)" onmouseout="bmou(this)" onclick="start()">&#160;</button>
</div>
</div>
</div>

<script>
      var CSIZE = 400;

var canvas = document.querySelector('#cta');
onresize = function () {
  canvas.style.maxHeight = window.innerHeight - 20 + 'px';
};

var ctx = canvas.getContext('2d');
ctx.translate(CSIZE, CSIZE);
ctx.rotate(-Math.PI / 2);
ctx.lineWidth = 4;

ctx.strokeStyle = 'hsl(180,90%,80%)';
ctx.fillStyle = 'hsla(0,0%,0%,0.05)';

onresize();

function powerRandom(p) {
  function rec(p, r) {
    --p;
    if (p <= 0) {
      return r;
    } else {
      r *= Math.random();
      return rec(p, r);
    }
  }
  p = Math.round(p);
  return rec(p, Math.random());
}

function getRandomInt(min, max, low) {
  var p = low ? low : 1;
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(powerRandom(p) * (max - min)) + min;
}

var primes = [2, 3, 5, 7, 11, 13, 17, 19];

var cycsets = {
  2: [2, 4, 6, 8, 10, 12, 14, 16, 18],
  3: [3, 6, 9, 12, 15, 18],
  5: [5, 10, 15],
  7: [7, 14],
  11: [11],
  13: [13],
  17: [17],
  19: [19] };


var cycsets2 = {
  2: [2, 4, 8, 16],
  3: [3, 9],
  5: [5],
  7: [7],
  11: [11],
  13: [13],
  17: [17],
  19: [19] };


function getFactors(n) {
  let a = [];
  for (let p of primes) {
    if (n % p == 0) {
      a.push(p);
    }
  }
  return a;
}

function getCycleArray(n) {
  let a = new Set();
  for (let p of primes) {
    if (n % p == 0) {
      for (let f of cycsets2[p]) {
        a.add(f);
      }
    }
  }
  return Array.from(a);
}

var Roulette = function (ro) {
  if (ro instanceof Roulette) {
    Object.assign(this, ro);
  } else {
    this.dz = -1;
    this.type1 = -1;
    this.type2 = -1;
    this.type3 = -1;
    this.type4 = -1;
    this.c0 = 1;
    this.c1 = 8;
    this.c2 = 16;
    this.c3 = 4;
    this.c4 = 4;
    this.cycleSet = 8;
    this.r1 = 100;
    this.r2 = 80;
    this.r3 = 40;
    this.r4 = 40;
    this.r5 = 40;
    this.radiiCount = 2;
  }
  let rself = this;
  this.getMetrics = function (rotFrac, n) {
    t = rself.dz * (rotFrac + n / ribbons.rCount) * rself.c0 * 2 * Math.PI;
    let f1 = 1 + rself.type1 * rself.c1 / rself.c0;
    var x, y;
    if (rself.radiiCount == 2) {
      x = rself.r1 * Math.cos(t) + rself.r2 * Math.cos(f1 * t);
      y = rself.r1 * Math.sin(t) + rself.r2 * Math.sin(f1 * t);
    } else if (rself.radiiCount == 3) {
      let f2 = 1 + (rself.type1 * rself.c1 + rself.type2 * rself.c2) / rself.c0;
      x = rself.r1 * Math.cos(t) + rself.r2 * Math.cos(f1 * t) + rself.r3 * Math.cos(f2 * t);
      y = rself.r1 * Math.sin(t) + rself.r2 * Math.sin(f1 * t) + rself.r3 * Math.sin(f2 * t);
    } else if (rself.radiiCount == 4) {
      let f2 = 1 + (rself.type1 * rself.c1 + rself.type2 * rself.c2) / rself.c0;
      let f3 = 1 + (rself.type1 * rself.c1 + rself.type2 * rself.c2 + rself.type3 * rself.c3) / rself.c0;
      x = rself.r1 * Math.cos(t) + rself.r2 * Math.cos(f1 * t) + rself.r3 * Math.cos(f2 * t) + rself.r4 * Math.cos(f3 * t);
      y = rself.r1 * Math.sin(t) + rself.r2 * Math.sin(f1 * t) + rself.r3 * Math.sin(f2 * t) + rself.r4 * Math.sin(f3 * t);
    } else if (rself.radiiCount == 5) {
      let f2 = 1 + (rself.type1 * rself.c1 + rself.type2 * rself.c2) / rself.c0;
      let f3 = 1 + (rself.type1 * rself.c1 + rself.type2 * rself.c2 + rself.type3 * rself.c3) / rself.c0;
      let f4 = 1 + (rself.type1 * rself.c1 + rself.type2 * rself.c2 + rself.type3 * rself.c3 + rself.type4 * rself.c4) / rself.c0;
      x = rself.r1 * Math.cos(t) + rself.r2 * Math.cos(f1 * t) + rself.r3 * Math.cos(f2 * t) + rself.r4 * Math.cos(f3 * t) + rself.r5 * Math.cos(f4 * t);
      y = rself.r1 * Math.sin(t) + rself.r2 * Math.sin(f1 * t) + rself.r3 * Math.sin(f2 * t) + rself.r4 * Math.sin(f3 * t) + rself.r5 * Math.sin(f4 * t);
    } else {
      //debugger;
    }
    return { x: x, y: y };
  };
  this.softCycle = function () {
    rself.c1 = rself.setCycles();
    rself.c2 = rself.setCycles();
    rself.c3 = rself.setCycles();
    rself.c4 = rself.setCycles();
  };
  this.setCycle0Match = function () {
    rself.c0 = getRandomInt(1, 19, 4);
  };
  this.getCycle0Match = function () {
    return getRandomInt(1, 19, 4);
  };

  this.setCycles = function () {
    //if (Math.random()<0.3) {  // put symmetry control here
    //let ca=getFactors(this.cycleSet);
    let ca = getCycleArray(this.cycleSet);
    if (ca.length == 0) {
      return this.cycleSet;
    }
    return ca[getRandomInt(0, ca.length)];
    /*
                                               } else if (Math.random()<0.3) {  // put symmetry control here
                                                 let ca=getFactors(ribbons.rCount);
                                                 if (ca.length==0) {
                                           	return this.cycleSet;
                                                 }
                                                 return ca[getRandomInt(0,ca.length)];
                                               } else {
                                                 let ca=getCycleArray(ribbons.rCount);
                                                 if (ca.length==0) {
                                           	return this.cycleSet;
                                                 }
                                                 return ca[getRandomInt(0,ca.length)];
                                               }
                                           */
  };
  this.randomizeRadiiCount = function () {
    //rself.radiiCount=2+Math.round(2*Math.random());
    rself.radiiCount = 2 + getRandomInt(0, 4);
  };
  this.randomizeRadii = function () {
    if (rself.radiiCount == 5) {
      rself.r1 = 70 - 20 * Math.random();
      rself.r2 = 20 + (160 - rself.r1) * Math.random();
      rself.r3 = 20 + (200 - rself.r1 - rself.r2) * Math.random();
      rself.r4 = 20 + (240 - rself.r1 - rself.r2 - rself.r3) * Math.random();
      rself.r5 = 280 - rself.r1 - rself.r2 - rself.r3 - rself.r4;
    } else if (rself.radiiCount == 4) {
      rself.r1 = 70 - 20 * Math.random();
      /*
                                                rself.r2=70-20*Math.random();
                                                rself.r3=70-20*Math.random();
                                                rself.r4=70-20*Math.random();
                                          */
      //      rself.r1=20+160*Math.random();
      rself.r2 = 20 + (200 - rself.r1) * Math.random();
      rself.r3 = 20 + (240 - rself.r1 - rself.r2) * Math.random();
      rself.r4 = 280 - rself.r1 - rself.r2 - rself.r3;
      rself.r5 = 0;
    } else if (rself.radiiCount == 3) {
      rself.r1 = 90 - 30 * Math.random();
      /*
                                                rself.r2=90-30*Math.random();
                                                rself.r3=90-30*Math.random();
                                          */
      //      rself.r1=20+160*Math.random();
      rself.r2 = 20 + (240 - rself.r1) * Math.random();
      rself.r3 = 280 - rself.r1 - rself.r2;
      rself.r4 = 0;
      rself.r5 = 0;
    } else if (rself.radiiCount == 2) {
      let rr = 210 + 70 * Math.random(); // sum of radii from 210-280
      let rd = 30 - 60 * Math.random();
      rself.r1 = (rd + rr) / 2;
      rself.r2 = (rr - rd) / 2;
      /*
                                      rself.r1=20+160*Math.random();
                                      rself.r2=280-rself.r1;
                                */
      rself.r3 = 0;
      rself.r4 = 0;
      rself.r5 = 0;
    } else {
      //debugger;
    }
  };
  this.randomizeCycles = function () {
    rself.cycleSet = ribbons.goodCycleSet();
    rself.c1 = rself.setCycles();
    rself.c2 = rself.setCycles();
    rself.c3 = rself.setCycles();
    rself.c4 = rself.setCycles();
    rself.c0 = getRandomInt(1, 19, 4);
  };
  this.randomizeTypes = function () {
    rself.type1 = [-1, 1][getRandomInt(0, 2)];
    rself.type2 = [-1, 1][getRandomInt(0, 2)];
    rself.type3 = [-1, 1][getRandomInt(0, 2)];
    rself.type4 = [-1, 1][getRandomInt(0, 2)];
  };
  this.getSP = function () {
    let sp = this.c0 + this.c1;
    switch (this.radiiCount) {
      case 2:
        return sp;
      case 3:
        return sp += this.c2;
      case 4:
        return sp += this.c2 + this.c3;
      case 5:
        return sp += this.c2 + this.c3 + this.c4;
      default:
        return 0;}

  };
  this.controlledCycleChange = function (mod) {
    let count = 0;
    let sMax = 24;
    let sMin = 15;
    do {
      mod();
      var sp = rself.getSP();
      if (count++ > 10) {
        return;
      }
      //} while (sp<rself.spMin || sp>rself.spMax);
    } while (sp > sMax++ || sp < sMin--);
  };
};

function cbLoc(p1, p2, frac) {
  var f1 = .2;
  var f2 = .8;
  var e1 = Math.pow(1 - frac, 3) * p1;
  var e2 = 3 * frac * Math.pow(1 - frac, 2) * (p1 + (p2 - p1) * f1);
  var e3 = 3 * (1 - frac) * Math.pow(frac, 2) * (p1 + (p2 - p1) * f2);
  var e4 = Math.pow(frac, 3) * p2;
  return e1 + e2 + e3 + e4;
}

var path = {
  fromRo: new Roulette(),
  toRo: new Roulette(),
  fromRoX: new Roulette(),
  toRoX: new Roulette(),
  start: 0,
  frac: 0,
  //duration:20000,
  duration: 5000,
  //duration:2000,
  getMetrics: function (rotFrac, n) {
    let fromMet = this.fromRo.getMetrics(rotFrac, n);
    let toMet = this.toRo.getMetrics(rotFrac, n);
    let fromMetX = this.fromRoX.getMetrics(rotFrac, n);
    let toMetX = this.toRoX.getMetrics(rotFrac, n);
    return {
      //x:this.frac*toMet.x+(1-this.frac)*fromMet.x,
      //y:this.frac*toMet.y+(1-this.frac)*fromMet.y,
      x: cbLoc(fromMet.x, toMet.x, this.frac),
      y: cbLoc(fromMet.y, toMet.y, this.frac),
      xX: cbLoc(fromMetX.x, toMetX.x, this.frac),
      yX: cbLoc(fromMetX.y, toMetX.y, this.frac) };

  },
  mf: 0.3,
  transit: function () {
    Object.assign(this.fromRo, this.toRo);
    Object.assign(this.fromRoX, this.toRoX);
    this.toRo = new Roulette(this.fromRo);
    this.toRoX = new Roulette(this.fromRoX);
    if (Math.random() < this.mf) {
      //this.toRo.randomizeCycles();
    } else {
      if (Math.random() < this.mf) {
        this.toRo.controlledCycleChange(this.toRo.setCycle0Match);
        //this.toRo.c0=this.toRo.getCycle0Match();
        //this.toRoX.c0=this.toRoX.getCycle0Match();
        this.toRoX.c0 = this.toRo.c0;
      }
      if (Math.random() < this.mf) {
        this.toRo.controlledCycleChange(this.toRo.softCycle);
        //this.toRo.softCycle();
        this.toRoX.c1 = this.toRo.c1;
        this.toRoX.c2 = this.toRo.c2;
        this.toRoX.c3 = this.toRo.c3;
        this.toRoX.c4 = this.toRo.c4;
      }
    }
    if (Math.random() < this.mf) {
      this.toRo.controlledCycleChange(this.toRo.randomizeRadiiCount);
      //this.toRo.randomizeRadiiCount();
      //this.toRoX.randomizeRadiiCount();
      this.toRoX.radiiCount = this.toRo.radiiCount;
    }
    if (Math.random() < this.mf) {
      this.toRo.randomizeTypes();
      this.toRoX.type1 = this.toRo.type1;
      this.toRoX.type2 = this.toRo.type2;
      this.toRoX.type3 = this.toRo.type3;
      this.toRoX.type4 = this.toRo.type4;
      //this.toRoX.randomizeTypes();
    }
    if (Math.random() < this.mf / 4) {
      this.toRo.dz = [-1, 1][getRandomInt(0, 2)];
      this.toRoX.dz = this.toRo.dz;
    }
    if (this.mf > 0) {
      this.toRo.randomizeRadii();
      this.toRoX.randomizeRadii();
    }
  },
  animate: function (ts) {
    if (stopped) {
      return;
    }
    if (!path.start) {
      path.start = ts;
    }
    let progress = ts - path.start;
    if (progress < path.duration) {
      path.frac .........完整代码请登录后点击上方下载按钮下载查看

网友评论0