根据音频跳动的粒子云

317 阅读1分钟

这是我参与11月更文挑战的第6天,活动详情查看:11月更文挑战

1.gif

<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/dat-gui/0.5/dat.gui.min.js"></script>
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/pixi.js/1.6.1/pixi.js"></script>
<h2 class="message">PLAY</h2>
html, body {
	margin:0;
  overflow:hidden;
  background:#000;
  color:white;
  font-family:Arial;
}

.message{
  padding:20px;
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%, -50%);
  background:black;
  cursor:pointer;
  font-size:14px;
  border:1px solid white;
}
(function() {
  var AUDIO_URL, TOTAL_BANDS, analyser, analyserDataArray, arrCircles, audio, build, buildCircles, canplay, changeMode, changeTheme, circlesContainer, cp, createCircleTex, gui, hammertime, init, initAudio, initGUI, initGestures, isPlaying, k, message, modes, mousePt, mouseX, mouseY, params, play, renderer, resize, stage, startAnimation, texCircle, themes, themesNames, update, v, windowH, windowW;

  AUDIO_URL = "https://www.jq22.com/demo/jqueryaudio201903252328/jq22.mp3";

  modes = ["cubic", "conic"];

  themes = {
    pinkBlue: [0xFF0032, 0xFF5C00, 0x00FFB8, 0x53FF00],
    yellowGreen: [0xF7F6AF, 0x9BD6A3, 0x4E8264, 0x1C2124, 0xD62822],
    yellowRed: [0xECD078, 0xD95B43, 0xC02942, 0x542437, 0x53777A],
    blueGray: [0x343838, 0x005F6B, 0x008C9E, 0x00B4CC, 0x00DFFC],
    blackWhite: [0xFFFFFF, 0x000000, 0xFFFFFF, 0x000000, 0xFFFFFF]
  };

  themesNames = [];

  for (k in themes) {
    v = themes[k];
    themesNames.push(k);
  }

  // PARAMETERS
  params = {
    // public
    mode: modes[0],
    theme: themesNames[0],
    radius: 3,
    distance: 600,
    size: .5,
    // private
    numParticles: 5000,
    sizeW: 1,
    sizeH: 1,
    radiusParticle: 60,
    themeArr: themes[this.theme]
  };

  TOTAL_BANDS = 256;

  cp = new PIXI.Point();

  mouseX = 0;

  mouseY = 0;

  mousePt = new PIXI.Point();

  windowW = 0;

  windowH = 0;

  stage = null;

  renderer = null;

  texCircle = null;

  circlesContainer = null;

  arrCircles = [];

  hammertime = null;

  message = null;

  // audio
  audio = null;

  analyser = null;

  analyserDataArray = null;

  isPlaying = false;

  canplay = false;

  // gui
  gui = null;

  init = function() {
    initGestures();
    message = $(".message");
    message.on("click", play);
    resize();
    build();
    resize();
    mousePt.x = cp.x;
    mousePt.y = cp.y;
    $(window).resize(resize);
    startAnimation();
    return initGUI();
  };

  play = function() {
    if (isPlaying) {
      return;
    }
    initAudio();
    message.css("cursor", "default");
    if (canplay) {
      message.hide();
    } else {
      message.html("LOADING MUSIC...");
    }
    audio.play();
    return isPlaying = true;
  };

  initGUI = function() {
    var modeController, sizeController, themeController;
    gui = new dat.GUI();
    // if window.innerWidth < 500
    gui.close();
    modeController = gui.add(params, 'mode', modes);
    modeController.onChange(function(value) {
      return changeMode(value);
    });
    themeController = gui.add(params, 'theme', themesNames);
    themeController.onChange(function(value) {
      return changeTheme(params.theme);
    });
    gui.add(params, 'radius', 1, 8);
    gui.add(params, 'distance', 100, 1000);
    sizeController = gui.add(params, 'size', 0, 1);
    return sizeController.onChange(function(value) {
      return resize(value);
    });
  };

  initAudio = function() {
    var context, source;
    context = new (window.AudioContext || window.webkitAudioContext)();
    analyser = context.createAnalyser();
    //   analyser.smoothingTimeConstant = 0.5
    source = null;
    audio = new Audio();
    audio.crossOrigin = "anonymous";
    audio.src = AUDIO_URL;
    return audio.addEventListener('canplay', function() {
      var bufferLength;
      if (isPlaying) {
        message.hide();
      }
      canplay = true;
      source = context.createMediaElementSource(audio);
      source.connect(analyser);
      source.connect(context.destination);
      analyser.fftSize = TOTAL_BANDS * 2;
      bufferLength = analyser.frequencyBinCount;
      return analyserDataArray = new Uint8Array(bufferLength);
    });
  };

  startAnimation = function() {
    return requestAnimFrame(update);
  };

  initGestures = function() {
    return $(window).on('mousemove touchmove', function(e) {
      if (e.type === 'mousemove') {
        mouseX = e.clientX;
        return mouseY = e.clientY;
      } else {
        mouseX = e.originalEvent.changedTouches[0].clientX;
        return mouseY = e.originalEvent.changedTouches[0].clientY;
      }
    });
  };

  build = function() {
    stage = new PIXI.Stage(0x000000);
    renderer = PIXI.autoDetectRenderer({
      width: $(window).width(),
      height: $(window).height(),
      antialias: true,
      resolution: window.devicePixelRatio
    });
    $(document.body).append(renderer.view);
    texCircle = createCircleTex();
    return buildCircles();
  };

  buildCircles = function() {
    var circle, i, j, ref;
    circlesContainer = new PIXI.DisplayObjectContainer();
    stage.addChild(circlesContainer);
    for (i = j = 0, ref = params.numParticles - 1; (0 <= ref ? j <= ref : j >= ref); i = 0 <= ref ? ++j : --j) {
      circle = new PIXI.Sprite(texCircle);
      circle.anchor.x = 0.5;
      circle.anchor.y = 0.5;
      circle.position.x = circle.xInit = cp.x;
      circle.position.y = circle.yInit = cp.y;
      circle.mouseRad = Math.random();
      circlesContainer.addChild(circle);
      arrCircles.push(circle);
    }
    return changeTheme(params.theme);
  };

  createCircleTex = function() {
    var gCircle;
    gCircle = new PIXI.Graphics();
    gCircle.beginFill(0xFFFFFF);
    gCircle.drawCircle(0, 0, params.radiusParticle);
    gCircle.endFill();
    return gCircle.generateTexture();
  };

  resize = function() {
    windowW = $(window).width();
    windowH = $(window).height();
    cp.x = windowW * .5;
    cp.y = windowH * .5;
    params.sizeW = windowH * params.size;
    params.sizeH = windowH * params.size;
    changeMode(params.mode);
    if (renderer) {
      return renderer.resize(windowW, windowH);
    }
  };

  changeTheme = function(name) {
    var circle, group, i, indexColor, j, padColor, ref, results;
    params.themeArr = themes[name];
    indexColor = 0;
    padColor = Math.ceil(params.numParticles / params.themeArr.length);
    results = [];
    for (i = j = 0, ref = params.numParticles - 1; (0 <= ref ? j <= ref : j >= ref); i = 0 <= ref ? ++j : --j) {
      circle = arrCircles[i];
      group = indexColor * padColor / params.numParticles;
      circle.blendMode = params.theme === "blackWhite" ? PIXI.blendModes.NORMAL : PIXI.blendModes.ADD;
      circle.indexBand = Math.round(group * (TOTAL_BANDS - 56)) - 1;
      if (circle.indexBand <= 0) {
        circle.indexBand = 49;
      }
      circle.s = (Math.random() + (params.themeArr.length - indexColor) * 0.2) * 0.1;
      circle.scale = new PIXI.Point(circle.s, circle.s);
      if (i % padColor === 0) {
        indexColor++;
      }
      results.push(circle.tint = params.themeArr[indexColor - 1]);
    }
    return results;
  };

  changeMode = function(value) {
    var angle, circle, i, j, ref, results;
    if (!arrCircles || arrCircles.length === 0) {
      return;
    }
    if (!value) {
      value = modes[Math.floor(Math.random() * modes.length)];
    }
    params.mode = value;
    results = [];
    for (i = j = 0, ref = params.numParticles - 1; (0 <= ref ? j <= ref : j >= ref); i = 0 <= ref ? ++j : --j) {
      circle = arrCircles[i];
      switch (params.mode) {
        // cubic
        case modes[0]:
          circle.xInit = cp.x + (Math.random() * params.sizeW - params.sizeW / 2);
          results.push(circle.yInit = cp.y + (Math.random() * params.sizeH - params.sizeH / 2));
          break;
        // circular
        case modes[1]:
          angle = Math.random() * (Math.PI * 2);
          circle.xInit = cp.x + (Math.cos(angle) * params.sizeW);
          results.push(circle.yInit = cp.y + (Math.sin(angle) * params.sizeH));
          break;
        default:
          results.push(void 0);
      }
    }
    return results;
  };

  update = function() {
    var a, angle, circle, dist, dx, dy, i, j, n, r, ref, scale, t, xpos, ypos;
    requestAnimFrame(update);
    t = performance.now() / 60;
    if (analyserDataArray && isPlaying) {
      analyser.getByteFrequencyData(analyserDataArray);
    }
    if (mouseX > 0 && mouseY > 0) {
      mousePt.x += (mouseX - mousePt.x) * 0.03;
      mousePt.y += (mouseY - mousePt.y) * 0.03;
    } else {
      a = t * 0.05;
      mousePt.x = cp.x + Math.cos(a) * 100;
      mousePt.y = cp.y + Math.sin(a) * 100;
    }
    for (i = j = 0, ref = params.numParticles - 1; (0 <= ref ? j <= ref : j >= ref); i = 0 <= ref ? ++j : --j) {
      circle = arrCircles[i];
      if (analyserDataArray && isPlaying) {
        n = analyserDataArray[circle.indexBand];
        scale = (n / 256) * circle.s * 2;
      } else {
        scale = circle.s * .1;
      }
      scale *= params.radius;
      circle.scale.x += (scale - circle.scale.x) * 0.3;
      circle.scale.y = circle.scale.x;
      dx = mousePt.x - circle.xInit;
      dy = mousePt.y - circle.yInit;
      dist = Math.sqrt(dx * dx + dy * dy);
      angle = Math.atan2(dy, dx);
      r = circle.mouseRad * params.distance + 30;
      xpos = circle.xInit - Math.cos(angle) * r;
      ypos = circle.yInit - Math.sin(angle) * r;
      circle.position.x += (xpos - circle.position.x) * 0.1;
      circle.position.y += (ypos - circle.position.y) * 0.1;
    }
    return renderer.render(stage);
  };

  init();

}).call(this);