Strange Tubes

旋转球体

  • 示例
  • HTML
  • CSS
  • JS

更多有趣示例 尽在小红砖社区

示例

HTML

<body class="text-center">
  <div id="page">
    <div class="cover-container d-flex h-100 p-3 mx-auto flex-column">
      <header class="masthead">
        <div>
          <h3 class="masthead-brand">Strange Tubesh3>
          <nav class="nav nav-masthead justify-content-center">
            <a class="nav-link" href="https://codepen.io/collection/AGZywR" target="_blank">ThreeJS Collectiona>
          nav>
        div>
      header>
      <main role="main">
        <form style="width: 80%; margin: 0 auto;">
          <div class="form-row">
            <div class="form-group col-sm-6">
              <label for="noiseInput" class="form-label">Noise Coeflabel>
              <input type="range" min="1" max="100" class="custom-range" id="noiseInput">
            div>
            <div class="form-group col-sm-6">
              <label for="heightInput" class="form-label">Height Coeflabel>
              <input type="range" min="1" max="100" class="custom-range" id="heightInput">
            div>
            <div class="form-group col-sm-6">
              <label for="timeInput" class="form-label">Time Coeflabel>
              <input type="range" min="1" max="100" class="custom-range" id="timeInput">
            div>
            <div class="form-group col-sm-6">
              <label for="tubeRadiusInput" class="form-label">Tube Radiuslabel>
              <input type="range" min="1" max="100" class="custom-range" id="tubeRadiusInput">
            div>
          div>
          <div class="form-group col-md-12">
            <div class="custom-control custom-switch">
              <input type="checkbox" class="custom-control-input" id="pRadiusInput" checked>
              <label class="custom-control-label" for="pRadiusInput">Progressive radiuslabel>
            div>
          div>
          <div class="form-group col-md-12">
            <a href="#" id="trigger" class="btn btn-sm btn-secondary">Random Colorsa>
          div>
        form>
      main>
    div>
  div>

  <canvas id="background">canvas>
body>

CSS

body {
     
  color: #fff; 
  background-color: #000;
  font-family: 'Montserrat', sans-serif;
  font-size: 0.8rem;
}

.cover-container {
     
  max-width: 42em;
}

.masthead {
     
  margin-bottom: 2rem;
}

.masthead-brand {
     
  margin-bottom: 0;
}

.nav-masthead .nav-link {
     
  padding: 0.25rem 0;
  color: #fff;
  background-color: transparent;
  border-bottom: 0.25rem solid transparent;
}

.nav-masthead .nav-link:hover,
.nav-masthead .nav-link:focus {
     
  border-bottom-color: rgba(255, 255, 255, 0.25);
}

.nav-masthead .nav-link+.nav-link {
     
  margin-left: 1rem;
}

main {
     
  display: none;
}

@media (min-width: 48em) {
     
  .masthead-brand {
     
    float: left;
  }
  .nav-masthead {
     
    float: right;
  }
  main {
     
    display: block;
  }
}

#background {
     
  position: fixed;
  z-index: -1;
  top: 0;
  left: 0;
}

JS

const simplex = new SimplexNoise();

function App(conf) {
     
  conf = {
     
    fov: 75,
    cameraZ: 150,
    tubeRadius: 3,
    tubeLength: 400,
    pRadius: true,
    resY: 6,
    resX: 12,
    noiseCoef: 30,
    timeCoef: 20,
    mouseCoef: 50,
    heightCoef: 30,
    colors: [0x390009, 0xe52643, 0xeadee0, 0xffffff],
    background: 0x000000,
    ambientColor: 0x909090,
    lightIntensity: 1,
    light1Color: 0xFFD700,
    light2Color: 0x00FFFF,
    light3Color: 0xffffff,
    ...conf
  };

  let renderer, scene, camera, cameraCtrl;
  let width, height, cx, cy, wWidth, wHeight;
  const TMath = THREE.Math;

  let light1, light2, light3;
  let objects, noiseConf = {
     };
  let cscale = chroma.scale(conf.colors);

  const mouse = new THREE.Vector2();
  const noiseInput = document.getElementById('noiseInput');
  const heightInput = document.getElementById('heightInput');
  const timeInput = document.getElementById('timeInput');
  const tubeRadiusInput = document.getElementById('tubeRadiusInput');
  const pRadiusInput = document.getElementById('pRadiusInput');

  init();

  function init() {
     
    renderer = new THREE.WebGLRenderer({
     
      canvas: document.getElementById(conf.el),
      antialias: true,
      // alpha: true,
    });
    camera = new THREE.PerspectiveCamera(conf.fov);
    camera.position.z = conf.cameraZ;

    updateSize();
    window.addEventListener('resize', updateSize, false);

    document.addEventListener('mousemove', e => {
     
      mouse.x = (e.clientX / width) * 2 - 1;
      mouse.y = -(e.clientY / height) * 2 + 1;
    });

    initScene();
    initGui();
    animate();
  }

  function initScene() {
     
    scene = new THREE.Scene();
    if (conf.background) scene.background = new THREE.Color(conf.background);
    initLights();
    initObjects();
    camera.position.z = 50;
    camera.position.y = -100;
    camera.lookAt(new THREE.Vector3(0, 50, 0));
  }

  function initLights() {
     
    scene.add(new THREE.AmbientLight(conf.ambientColor));

    const z = 50;
    const lightDistance = 500;
    light1 = new THREE.PointLight(conf.light1Color, conf.lightIntensity, lightDistance);
    light1.position.set(0, wHeight / 2, z);
    scene.add(light1);
    light2 = new THREE.PointLight(conf.light2Color, conf.lightIntensity, lightDistance);
    light2.position.set(0, -wHeight / 2, z);
    scene.add(light2);
    light3 = new THREE.PointLight(conf.light3Color, conf.lightIntensity, lightDistance);
    light3.position.set(wWidth / 2, 0, z);
    scene.add(light3);
  }

  function initObjects() {
     
    updateNoise();
    const nx = Math.round(wWidth / conf.resX) + 1;
    const ny = Math.round(conf.tubeLength / conf.resY) + 1;
    objects = [];
    let tube, color;
    for (let i = 0; i < nx; i++) {
     
      // color = cscale(j/ny).hex();
      color = cscale(TMath.randFloat(0, 1)).hex();
      tube = new Tube(-wWidth / 2 + i * conf.resX, -wHeight / 2, conf.tubeLength, ny, conf.tubeRadius, color, noiseConf);
      objects.push(tube);
      scene.add(tube.mesh);
    }
  }

  function initGui() {
     
    noiseInput.value = conf.noiseCoef;
    heightInput.value = conf.heightCoef;
    timeInput.value = conf.timeCoef;
    tubeRadiusInput.value = conf.tubeRadius * 10;

    noiseInput.addEventListener('input', e => {
      conf.noiseCoef = noiseInput.value; });
    heightInput.addEventListener('input', e => {
      conf.heightCoef = heightInput.value; });
    timeInput.addEventListener('input', e => {
      conf.timeCoef = timeInput.value; });
    tubeRadiusInput.addEventListener('input', e => {
      conf.tubeRadius = tubeRadiusInput.value / 10; });
    pRadiusInput.addEventListener('input', e => {
      conf.pRadius = pRadiusInput.checked; });

    document.getElementById('trigger').addEventListener('click', e => {
      updateColors(); });
  }

  function updateNoise() {
     
    noiseConf.coef = conf.noiseCoef * 0.00015;
    noiseConf.height = conf.heightCoef;
    noiseConf.time = Date.now() * conf.timeCoef * 0.000005;
    noiseConf.mouseX = mouse.x / 3;
    noiseConf.mouseY = mouse.y / 3;
    // noiseConf.mouse = (mouse.x + mouse.y) * conf.mouseCoef * 0.005;
  }

  function updateColors() {
     
    conf.light4Color = chroma.random().hex();
    light1.color = new THREE.Color(chroma.random().hex());
    light2.color = new THREE.Color(chroma.random().hex());
    light3.color = new THREE.Color(chroma.random().hex());
    updateCScale();
  }

  function updateCScale() {
     
    const color = chroma.random();
    cscale = chroma.scale([
      color.set('hsl.s', TMath.randFloat(0, 1)).set('hsl.l', TMath.randFloat(0, 0.3)).hex(),
      color.set('hsl.s', TMath.randFloat(0, 1)).set('hsl.l', 0.3 + TMath.randFloat(0, 0.4)).hex(),
      color.set('hsl.s', TMath.randFloat(0, 1)).set('hsl.l', 0.7 + TMath.randFloat(0, 0.3)).hex(),
      0xffffff,
    ]);
    for (let i = 0; i < objects.length; i++) {
     
      objects[i].material.color = new THREE.Color(cscale(TMath.randFloat(0, 1)).hex());
    }
  }

  function animate() {
     
    requestAnimationFrame(animate);

    const time = Date.now() * 0.001;
    const dx = wWidth / 2;
    const dy = wHeight / 2;
    light1.position.x = Math.sin(time * 0.1) * dx;
    light1.position.y = Math.cos(time * 0.2) * dy;
    light2.position.x = Math.cos(time * 0.3) * dx;
    light2.position.y = Math.sin(time * 0.4) * dy;
    light3.position.x = Math.sin(time * 0.5) * dx;
    light3.position.y = Math.sin(time * 0.6) * dy;

    updateNoise();
    for (let i = 0; i < objects.length; i++) {
     
      objects[i].update(conf);
    }

    renderer.render(scene, camera);
  }

  function updateSize() {
     
    width = window.innerWidth;
    cx = width / 2;
    height = window.innerHeight;
    cy = height / 2;
    if (renderer && camera) {
     
      renderer.setSize(width, height);
      camera.aspect = width / height;
      camera.updateProjectionMatrix();
      const wsize = getRendererSize();
      wWidth = wsize[0];
      wHeight = wsize[1];
    }
  }

  function getRendererSize() {
     
    const cam = new THREE.PerspectiveCamera(camera.fov, camera.aspect);
    const vFOV = (cam.fov * Math.PI) / 180;
    const height = 2 * Math.tan(vFOV / 2) * Math.abs(conf.cameraZ);
    const width = height * cam.aspect;
    return [width, height];
  }
}

/**
  * Custom curve
  */
function CustomCurve(x, y, l, noise) {
     
  THREE.Curve.call(this);
  this.x = x; this.y = y; this.l = l;
  this.noise = noise;
  this.xn = this.x * this.noise.coef;
}
CustomCurve.prototype = Object.create(THREE.Curve.prototype);
CustomCurve.prototype.constructor = CustomCurve;
CustomCurve.prototype.getPoint = function (t) {
     
  let y = this.y + t * this.l;
  let yn = y * this.noise.coef;
  let noise1 = simplex.noise2D(this.xn + this.noise.mouseX, yn - this.noise.time + this.noise.mouseY);
  let noise2 = simplex.noise2D(yn + this.noise.time, this.xn - this.noise.time);
  let x = this.x + noise2 * this.noise.height;
  let z = noise1 * this.noise.height;
  return new THREE.Vector3(x, y, z);
};

/**
  * Tube class
  */
class Tube {
     
  constructor(x, y, l, segments, radius, color, noise) {
     
    this.segments = segments;
    this.radialSegments = 8;
    this.radius = radius;

    this.curve = new CustomCurve(x, y, l, noise);
    this.geometry = new THREE.TubeBufferGeometry(this.curve, segments, radius, this.radialSegments, false);
    // this.material = new THREE.MeshBasicMaterial({ color });
    // this.material = new THREE.MeshLambertMaterial({ color });
    this.material = new THREE.MeshStandardMaterial({
      color });
    this.mesh = new THREE.Mesh(this.geometry, this.material);
  }
  update(conf) {
     
    this.radius = conf.tubeRadius;

    this.frames = this.curve.computeFrenetFrames(this.segments, false);
    // this.geometry.tangents = frames.tangents;
    // this.geometry.normals = frames.normals;
    // this.geometry.binormals = frames.binormals;

    this.pArray = this.geometry.attributes.position.array;
    this.nArray = this.geometry.attributes.normal.array;
    this.P = new THREE.Vector3();
    this.normal = new THREE.Vector3();
    for (let i = 0; i < this.segments; i++) {
     
      this.updateSegment(i, conf.pRadius);
    }
    this.updateSegment(this.segments, conf.pRadius);

    this.geometry.attributes.position.needsUpdate = true;
    this.geometry.attributes.normal.needsUpdate = true;
  }
  updateSegment(i, pRadius) {
     
    this.P = this.curve.getPointAt(i / this.segments, this.P);
    const r = pRadius ? this.radius - (i * this.radius / this.segments) : this.radius;
    const N = this.frames.normals[i];
    const B = this.frames.binormals[i];
    for (let j = 0; j <= this.radialSegments; j++) {
     
      let v = j / this.radialSegments * Math.PI * 2;
      let sin = Math.sin(v);
      let cos = - Math.cos(v);
      this.normal.x = (cos * N.x + sin * B.x);
      this.normal.y = (cos * N.y + sin * B.y);
      this.normal.z = (cos * N.z + sin * B.z);
      this.normal.normalize();
      let index = (i * (this.radialSegments + 1) + j) * 3;
      this.nArray[index] = this.normal.x;
      this.nArray[index + 1] = this.normal.y;
      this.nArray[index + 2] = this.normal.z;
      this.pArray[index] = this.P.x + r * this.normal.x;
      this.pArray[index + 1] = this.P.y + r * this.normal.y;
      this.pArray[index + 2] = this.P.z + r * this.normal.z;
    }
  }
}

App({
      el: 'background' });

你可能感兴趣的:(#,页面布置,陌生管道)