Focusing on the sample code from the response
I experimented a bit with the code, I scattered the points on the screen
textGeo.vertices.forEach(function (vertex) { vertex.x = THREE.Math.randFloat(-20, 20)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5); vertex.y = THREE.Math.randFloat(-20, 20)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5); vertex.z = THREE.Math.randFloat(-10, 10)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5); }); Added a couple of functions and changed the render function.
function setPosition(vertex, curent, orign, step) { let curentPoint = Math.round(curent * 10) / 10; let orignPoint = Math.round(orign * 10) / 10; if (curentPoint !== orignPoint) { curent = orign < curent ? curent - step : curent + step; } else { curent = orign; vertex.complate = true; } return curent; } function isComplated(vertices) { let result = true; vertices.forEach(function(vertex, i) { if(!vertex.complate) { result = false; } }); return result; } function render() { let animationId = requestAnimationFrame(render); if (!isComplated(textGeo.vertices)) { textGeo.vertices.forEach(function (vertex) { vertex.x = setPosition(vertex, vertex.x, vertex.startPoint.x, 0.1); vertex.y = setPosition(vertex, vertex.y, vertex.startPoint.y, 0.1); vertex.z = setPosition(vertex, vertex.z, vertex.startPoint.z, 0.1); }); } else { cancelAnimationFrame(animationId); } textGeo.verticesNeedUpdate = true; renderer.render(scene, camera); } The idea was as follows:
We scatter the points on the screen, and then we begin to return them to the initial places, when all the points will take their initial position to complete the animation by stopping the AnimationFrame. But the problem is that cancelAnimationFrame (animationId) is called before all points return to their places.
Working example:
var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); camera.position.set(0, 10, 20); var renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // var controls = new OrbitControls(camera, renderer.domElement); camera.lookAt(scene.position); var light = new THREE.DirectionalLight(0xffffff, 2); light.position.setScalar(100); scene.add(light); var textGeo = null; var textPoints = null; var loader = new THREE.FontLoader(); loader.load('https://threejs.org/examples/fonts/droid/droid_serif_bold.typeface.json', function (response) { var font = response; setText(font); render(); }); function setText(font) { textGeo = new THREE.TextGeometry('ABC', { font: font, size: 4, height: 0.5, curveSegments: 4, bevelEnabled: false, bevelSize: 10, bevelThickness: 50 }); textGeo.computeBoundingBox(); textGeo.computeVertexNormals(); textGeo.center(); fillWithPoints(textGeo, 10); textGeo.vertices.forEach(function (vertex) { vertex.startPoint = vertex.clone(); vertex.direction = vertex.clone().normalize(); }) textGeo.vertices.forEach(function (vertex) { vertex.x = THREE.Math.randFloat(-20, 20)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5); vertex.y = THREE.Math.randFloat(-20, 20)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5); vertex.z = THREE.Math.randFloat(-10, 10)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5); }); window.p = textGeo.vertices[0] //console.log(textGeo.vertices[0]); //textGeo.verticesNeedUpdate = true; //textGeo.applyMatrix( new THREE.Matrix4().makeTranslation( 1, 1, 1 ) ); textPoints = new THREE.Points(textGeo, new THREE.PointsMaterial({ color: 0xf00008, size: 0.1 })); scene.add(textPoints); } function fillWithPoints(geometry, pointNumber) { geometry.computeBoundingBox(); for (var i = 0; i < pointNumber; i++) { setRandomPoint(geometry); } } function setRandomPoint(geometry) { var point = new THREE.Vector3( THREE.Math.randFloat(geometry.boundingBox.min.x, geometry.boundingBox.max.x), THREE.Math.randFloat(geometry.boundingBox.min.y, geometry.boundingBox.max.y), THREE.Math.randFloat(geometry.boundingBox.min.z, geometry.boundingBox.max.z) ); //console.log(point); if (isPointInside(point, geometry)) { geometry.vertices.push(point); } else { setRandomPoint(geometry); } } var a = new THREE.Vector3(); var b = new THREE.Vector3(); var c = new THREE.Vector3(); var face = new THREE.Face3(); function isPointInside(point, geometry) { var retVal = false; for (var i = 0; i < geometry.faces.length; i++) { face = geometry.faces[i]; a = geometry.vertices[face.a]; b = geometry.vertices[face.b]; c = geometry.vertices[face.c]; //console.log(face, a, b, c); if (ptInTriangle(point, a, b, c)) { var retVal = true; break; } } return retVal; } function ptInTriangle(p, p0, p1, p2) { // credits: http://jsfiddle.net/PerroAZUL/zdaY8/1/ var A = 1 / 2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y); var sign = A < 0 ? -1 : 1; var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * px + (p0.x - p2.x) * py) * sign; var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * px + (p1.x - p0.x) * py) * sign; return s > 0 && t > 0 && (s + t) < 2 * A * sign; } function setPosition(vertex, curent, orign, step) { let curentPoint = Math.round(curent * 10) / 10; let orignPoint = Math.round(orign * 10) / 10; if (curentPoint !== orignPoint) { curent = orign < curent ? curent - step : curent + step; } else { curent = orign; vertex.complate = true; } return curent; } function isComplated(vertices) { let result = true; vertices.forEach(function(vertex, i) { if(!vertex.complate) { result = false; } }); return result; } function render() { let animationId = requestAnimationFrame(render); if (!isComplated(textGeo.vertices)) { textGeo.vertices.forEach(function (vertex) { vertex.x = setPosition(vertex, vertex.x, vertex.startPoint.x, 0.1); vertex.y = setPosition(vertex, vertex.y, vertex.startPoint.y, 0.1); vertex.z = setPosition(vertex, vertex.z, vertex.startPoint.z, 0.1); }); } else { cancelAnimationFrame(animationId); } textGeo.verticesNeedUpdate = true; /* complated = isComplated(textGeo.vertices); if (complated) { //console.log(complated); cancelAnimationFrame(animationId); console.log('end') } */ renderer.render(scene, camera); } <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script> Am I going the right way? Or are such things done differently?