Reputation: 333
I'm trying to animate the TextGeometries to form a shape of box {delay} sphere {delay} and then a cone. Also, change the colors while the transition. I tried the below code for trying to animate the initial state to a box:
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 ++ ]
})
}
The console throws the error below:
particleGroup.children[i] is undefined
I checked the particleGroup
has objects, however I'm getting this error. I'm not sure why this isn't working.
Currently all the particles are in the center, I'm trying to animate them to form a shape of a sphere, then a delay before the next shape animation and so on. Repeat the loop at the end. If someone could point me in the right direction, it'd be much appreciated. Thank you!
Below is the code:
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>
Upvotes: 2
Views: 133
Reputation: 11293
I added a slight delay to animationSequence
setTimeout(() => this.animationSequence(), 1000);
This is not the best approach and ideally you'd wait until particleGroup.children
is loaded with data
After bit of investigation I found the culprit.
addText: function() {
loader.load(typeface, (font) => {
Wrap that in Promise
addText: function() {
return new Promise((resolve, reject) => {
loader.load(typeface, (font) => {
Use put rest of logic in then callback
this.addText().then(_ => {
this.animate();
this.animationSequence();
})
Working example
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>
Upvotes: 3