Three.js TimeLineLite 动画不工作

Three.js TimeLineLite animation not working

我正在尝试为 TextGeometries 设置动画,以形成一个盒子 {delay} 球体 {delay} 的形状,然后是一个圆锥体。另外,在过渡时更改颜色。我尝试了以下代码来尝试将初始状态动画化为一个框:

var pointsInsideShape = shapes[0].points.attributes.position.array;
for (i = 0; i < MAX_PARTICLES; i++) {
  var tl = new TimelineLite();
  tl.from(particleGroup.children[i].position, 2, {
    x: pointsInsideShape[ index ++ ],
    y: pointsInsideShape[ index ++ ],
    z: pointsInsideShape[ index ++ ]
  })
}

控制台抛出以下错误:

particleGroup.children[i] is undefined

我检查了 particleGroup 有对象,但是我收到了这个错误。我不确定为什么这不起作用。

目前所有粒子都在中心,我正在尝试将它们设置为动画以形成球体形状,然后在下一个形状动画之前延迟等等。最后重复循环。如果有人能指出我正确的方向,我将不胜感激。谢谢!

代码如下:

var renderer, camera, scene, light, shapes, triggers, particleCount, particleGroup,
  defaultAnimationSpeed, morphAnimationSpeed, colorToStartWith, textGeometries, loader, typeface,
  animationVars;

const MAX_PARTICLES = 100,
  PARTICLE_SIZE = .65;

var sts = {
  config: function() {
    shapes = [{
        "geoCode": new THREE.BoxBufferGeometry(13, 13, 13),
        "textMat": new THREE.MeshPhongMaterial({
          color: 0x029894
        }),
        "color": 0x029894,
        "size": [50, 50, 50]
      },
      {
        "geoCode": new THREE.SphereBufferGeometry(25, 33, 33),
        "textMat": new THREE.MeshPhongMaterial({
          color: 0x8F3985
        }),
        "color": 0x8F3985,
        "size": [25, 33, 33]
      },
      {
        "geoCode": new THREE.ConeBufferGeometry(25, 50, 30),
        "textMat": new THREE.MeshPhongMaterial({
          color: 0x11659C
        }),
        "color": 0x11659C,
        "size": [25, 50, 30]
      }
    ];
    triggers = document.getElementsByTagName('span');
    particleGroup = new THREE.Group();
    defaultAnimationSpeed = 1;
    morphAnimationSpeed = 18;
    normalSpeed = (defaultAnimationSpeed / 100)
    fullSpeed = (morphAnimationSpeed / 100)
    colorToStartWith = '#8F3985';
    textGeometries = new Array();
  },
  initScene: function() {

    //Renderer
    renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    renderer.setClearColor(0xffffff, 1);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // Scene
    scene = new THREE.Scene();

    // Camera and position
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.y = -25;
    camera.position.z = 45;
    camera.rotation.x = -.45;

    // Lighting
    light = new THREE.DirectionalLight(0xffffff, 1);
    light.position.set(1, 1, 1).normalize();
    scene.add(light);
    var light2 = new THREE.DirectionalLight(0xffffff, 1.5);
    light2.position.set(0, -1, 0);
    scene.add(light2);

    // Texts
    loader = new THREE.FontLoader();
    typeface = 'https://raw.githubusercontent.com/7dir/json-fonts/master/fonts/cyrillic/roboto/Roboto_Regular.json';

    //particleGroup
    particleGroup = new THREE.Group();
    particleGroup.position.x = 0;
    particleGroup.position.y = -45;
    particleGroup.position.z = 0;
    scene.add(particleGroup);
  },
  fullScreen: function() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  },
  attachEvents: function() {
    window.addEventListener('resize', this.fullScreen, false);
  },
  addShapes: function() {
    for (idx = 0; idx < shapes.length; idx++) {
      shapes[idx].geometry = shapes[idx].geoCode.toNonIndexed();
      m = new THREE.MeshLambertMaterial({
        color: shapes[idx].color,
        opacity: 0,
        transparent: true,
        wireframe: true
      });
      shapes[idx].geometry.center();
      shape = new THREE.Mesh(shapes[idx].geometry, m);
      scene.add(shape);
    }
  },
  computePointsInside: function(idx) {
    shapes[idx].points = this.fillWithPoints(shapes[idx].geometry, MAX_PARTICLES);
  },
  addText: function() {
    loader.load(typeface, (font) => {
      //var x, y, z, index1, index2;
      //x = y = z = index1 = index2 = 0;
      var m = new THREE.MeshPhongMaterial({
        color: 0x8F3985,
        opacity: .8,
        specular: 0xffffff,
        shininess: 100
      });
      for (i = 0; i < MAX_PARTICLES; i++) {
        g = new THREE.TextGeometry(Math.random() < .5 ? '6' : '9', {
          font: font,
          size: PARTICLE_SIZE,
          height: 0.1
        });

        var text = new THREE.Mesh(g, m);
        text.position.x = 0;
        text.position.y = 0;
        text.position.z = 0;

        text.rotation.x = Math.random() * 2 * Math.PI;
        text.rotation.y = Math.random() * 2 * Math.PI;
        text.rotation.z = Math.random() * 2 * Math.PI;

        text.scale.x = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
        text.scale.y = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
        text.scale.z = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);

        //var pointsInsideShape = shapes[0].points.attributes.position.array;

        //text.position.x = pointsInsideShape[ index2 ++ ];
        //text.position.y = pointsInsideShape[ index2 ++ ];
        //text.position.z = pointsInsideShape[ index2 ++ ];

        particleGroup.add(text);
      }
    });
  },
  animationSequence: function() {
    var x, y, z, index;
    x = y = z = index = 0;
    var pointsInsideShape = shapes[0].points.attributes.position.array;
    for (i = 0; i < MAX_PARTICLES; i++) {
      var tl = new TimelineLite();
      tl.from(particleGroup.children[i].position, 2, {
        x: pointsInsideShape[index++],
        y: pointsInsideShape[index++],
        z: pointsInsideShape[index++]
      })
    }
  },
  animate: function() {
    window.requestAnimationFrame(sts.animate);
    particleGroup.rotation.y += 0.005;
    renderer.render(scene, camera)
  },
  fillWithPoints: function(geometry, count) {

    var ray = new THREE.Ray()

    var size = new THREE.Vector3();
    geometry.computeBoundingBox();
    var bbox = geometry.boundingBox;

    var points = [];

    var dir = new THREE.Vector3(1, 1, 1).normalize();
    for (var i = 0; i < count; i++) {
      var p = setRandomVector(bbox.min, bbox.max);
      points.push(p);
    }

    function setRandomVector(min, max) {
      var v = new THREE.Vector3(
        THREE.Math.randFloat(min.x, max.x),
        THREE.Math.randFloat(min.y, max.y),
        THREE.Math.randFloat(min.z, max.z)
      );
      if (!isInside(v)) {
        return setRandomVector(min, max);
      }
      return v;
    }

    function isInside(v) {

      ray.set(v, dir);
      var counter = 0;

      var pos = geometry.attributes.position;
      var faces = pos.count / 3;
      var vA = new THREE.Vector3(),
        vB = new THREE.Vector3(),
        vC = new THREE.Vector3();

      for (var i = 0; i < faces; i++) {
        vA.fromBufferAttribute(pos, i * 3 + 0);
        vB.fromBufferAttribute(pos, i * 3 + 1);
        vC.fromBufferAttribute(pos, i * 3 + 2);
        if (ray.intersectTriangle(vA, vB, vC)) counter++;
      }

      return counter % 2 == 1;
    }

    return new THREE.BufferGeometry().setFromPoints(points);
  },
  init: function() {
    this.config();
    this.initScene();
    this.attachEvents();
    this.addShapes();
    this.computePointsInside(0);
    this.computePointsInside(1);
    this.computePointsInside(2);
    this.addText();
    this.animate();
    this.animationSequence();
  }
}

sts.init();
body {
  margin: 0;
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js" type="text/javascript"></script>

我在 animationSequence

中添加了一点延迟
setTimeout(() => this.animationSequence(), 1000);

这不是最好的方法,理想情况下你会等到 particleGroup.children 加载数据

经过一些调查,我找到了罪魁祸首。

addText: function() {
    loader.load(typeface, (font) => {

将其包装在 Promise 中

addText: function() {
    return new Promise((resolve, reject) => {
    loader.load(typeface, (font) => {

在回调中使用剩下的逻辑

this.addText().then(_ => {
      this.animate();
      this.animationSequence();
    })

工作示例

var renderer, camera, scene, light, shapes, triggers, particleCount, particleGroup,
defaultAnimationSpeed, morphAnimationSpeed, colorToStartWith, textGeometries, loader, typeface,
animationVars;

const MAX_PARTICLES = 100, PARTICLE_SIZE = .65;

var sts = {
  config: function() {
    shapes = [
      {
        "geoCode": new THREE.BoxBufferGeometry(13, 13, 13),
        "textMat": new THREE.MeshPhongMaterial({ color: 0x029894 }),
        "color": 0x029894,
        "size": [50, 50, 50]
      },
      {
        "geoCode": new THREE.SphereBufferGeometry(25, 33, 33),
        "textMat": new THREE.MeshPhongMaterial({ color: 0x8F3985 }),
        "color": 0x8F3985,
        "size": [25, 33, 33]
      },
      {
        "geoCode": new THREE.ConeBufferGeometry(25, 50, 30),
        "textMat": new THREE.MeshPhongMaterial({ color: 0x11659C }),
        "color": 0x11659C,
        "size": [25, 50, 30]
      }
    ];
    triggers = document.getElementsByTagName('span');
    particleGroup = new THREE.Group();
    defaultAnimationSpeed = 1;
    morphAnimationSpeed = 18;
    normalSpeed = (defaultAnimationSpeed / 100),
    fullSpeed = (morphAnimationSpeed / 100)
    colorToStartWith = '#8F3985';
    textGeometries = new Array();
  },
  initScene: function() {

    //Renderer
    renderer = new THREE.WebGLRenderer({antialias: true});
    renderer.setClearColor(0xffffff, 1);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // Scene
    scene = new THREE.Scene();

    // Camera and position
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.y = -25;
    camera.position.z = 45;
    camera.rotation.x = -.45;

    // Lighting
    light = new THREE.DirectionalLight( 0xffffff, 1 );
  light.position.set( 1, 1, 1 ).normalize();
    scene.add(light);
  var light2 = new THREE.DirectionalLight( 0xffffff, 1.5 );
  light2.position.set( 0, - 1, 0 );
  scene.add( light2 );

    // Texts
    loader = new THREE.FontLoader();
    typeface = 'https://raw.githubusercontent.com/7dir/json-fonts/master/fonts/cyrillic/roboto/Roboto_Regular.json';

    //particleGroup
    particleGroup = new THREE.Group();
    particleGroup.position.x = 0;
    particleGroup.position.y = -45;
    particleGroup.position.z = 0;
    scene.add(particleGroup);
  },
  fullScreen: function() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  },
  attachEvents: function() {
    window.addEventListener('resize', this.fullScreen, false);
  },
  addShapes: function() {
    for (idx = 0; idx < shapes.length; idx++) {
      shapes[idx].geometry = shapes[idx].geoCode.toNonIndexed();
      m = new THREE.MeshLambertMaterial({
        color: shapes[idx].color,
        opacity: 0,
        transparent: true,
        wireframe: true
      });
      shapes[idx].geometry.center();
      shape = new THREE.Mesh(shapes[idx].geometry, m);
      scene.add(shape);
    }
  },
  computePointsInside: function(idx) {
    shapes[idx].points = this.fillWithPoints(shapes[idx].geometry, MAX_PARTICLES);
  },
  addText: function() {
    return new Promise((resolve, reject) => {
      loader.load(typeface, (font) => {
        //var x, y, z, index1, index2;
        //x = y = z = index1 = index2 = 0;
        var m = new THREE.MeshPhongMaterial({color: 0x8F3985, opacity: .8, specular: 0xffffff, shininess: 100});
        for (i = 0; i < MAX_PARTICLES; i++) {
          g = new THREE.TextGeometry( Math.random() < .5 ? '6' : '9', {
            font: font,
            size: PARTICLE_SIZE,
            height: 0.1
          });

          var text = new THREE.Mesh(g, m);
          text.position.x = 0;
          text.position.y = 0;
          text.position.z = 0;

          text.rotation.x = Math.random() * 2 * Math.PI;
          text.rotation.y = Math.random() * 2 * Math.PI;
          text.rotation.z = Math.random() * 2 * Math.PI;

          text.scale.x = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
          text.scale.y = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
          text.scale.z = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);

          //var pointsInsideShape = shapes[0].points.attributes.position.array;

          //text.position.x = pointsInsideShape[ index2 ++ ];
          //text.position.y = pointsInsideShape[ index2 ++ ];
          //text.position.z = pointsInsideShape[ index2 ++ ];

          particleGroup.add(text);
        }
        resolve();
      });
    })
  },
  animationSequence: function() {
    var x, y, z, index;
    x = y = z = index = 0;
    
    const children = particleGroup.children;
    
    var pointsInsideShape = shapes[0].points.attributes.position.array;
    for (i = 0; i < MAX_PARTICLES; i++) {
      var tl = new TimelineLite();
      const child = children[i];
      
      tl.from(child.position, 2, {
        x: pointsInsideShape[ index ++ ],
        y: pointsInsideShape[ index ++ ],
        z: pointsInsideShape[ index ++ ]
      })
    }
  },
  animate: function() {
    window.requestAnimationFrame(sts.animate);
    particleGroup.rotation.y +=0.005;
    renderer.render(scene, camera)
  },
  fillWithPoints: function(geometry, count) {
    
    var ray = new THREE.Ray()
    
    var size = new THREE.Vector3();
    geometry.computeBoundingBox();
    var bbox = geometry.boundingBox;
    
    var points = [];
    
    var dir = new THREE.Vector3(1, 1, 1).normalize();
    for (var i = 0; i < count; i++) {
      var p = setRandomVector(bbox.min, bbox.max);
      points.push(p);
    }
    
    function setRandomVector(min, max){
      var v = new THREE.Vector3(
        THREE.Math.randFloat(min.x, max.x),
        THREE.Math.randFloat(min.y, max.y),
        THREE.Math.randFloat(min.z, max.z)
      );
      if (!isInside(v)){return setRandomVector(min, max);}
      return v;
    }
    
    function isInside(v){
      
      ray.set(v, dir);
      var counter = 0;

      var pos = geometry.attributes.position;
      var faces = pos.count / 3;
      var vA = new THREE.Vector3(), vB = new THREE.Vector3(), vC = new THREE.Vector3();
  
      for(var i = 0; i < faces; i++){
        vA.fromBufferAttribute(pos, i * 3 + 0);
        vB.fromBufferAttribute(pos, i * 3 + 1);
        vC.fromBufferAttribute(pos, i * 3 + 2);
        if (ray.intersectTriangle(vA, vB, vC)) counter++;
      }
      
      return counter % 2 == 1;
    }
    
    return new THREE.BufferGeometry().setFromPoints(points);
  },
  init: function() {
    this.config();
    this.initScene();
    this.attachEvents();
    this.addShapes();
    this.computePointsInside(0);
    this.computePointsInside(1);
    this.computePointsInside(2);
    this.addText().then(_ => {
      this.animate();
      this.animationSequence();
    })
  }
}

sts.init();
body {
  margin: 0;
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js" type="text/javascript"></script>