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?

  • Do not use snippet for code snippets. Just mark as a code. - Kromster

1 answer 1

The random position of a point is known, the final position of a point is known, the distance between them is known, and the speed is known. We can find the time of the animation by dividing the greatest of all distances by speed, and as soon as the current time of the animation has become greater or equal to the calculated one, we perform some kind of action.

In addition, there is a .clampLength() method that simplifies life and does not give the length of the increment vector to go beyond the specified limits:

 var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); camera.position.set(2, 3, 5); var renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); var controls = new THREE.OrbitControls(camera, renderer.domElement); var tempDist = new THREE.Vector3(); var boxGeom = new THREE.BoxGeometry(2, 2, 2, 10, 10, 10); boxGeom.vertices.forEach(v => { v.init = v.clone(); v.random = new THREE.Vector3(THREE.Math.randFloatSpread(10), THREE.Math.randFloatSpread(10), THREE.Math.randFloatSpread(5)); v.dir = new THREE.Vector3().copy(v.init).sub(v.random).normalize(); v.dist = tempDist.copy(v.init).sub(v.random).length(); v.copy(v.random); }); var boxMat = new THREE.PointsMaterial({ size: 0.1, color: "red" }); var box = new THREE.Points(boxGeom, boxMat); scene.add(box); var speed = 2; // Π΅Π΄ΠΈΠ½ΠΈΡ† Π² сСкунду var longestDist = 0; boxGeom.vertices.forEach(v => { longestDist = Math.max(longestDist, v.dist); }); var fullTime = longestDist / speed; // ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ Π°Π½ΠΈΠΌΠ°Ρ†ΠΈΠΈ опрСдСляСтся ΠΏΠΎ самой Π΄Π»ΠΈΠ½Π½ΠΎΠΉ дистанции, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ постоянная console.log({ fullTime }); var clock = new THREE.Clock(); var delta = 0; var globalTime = 0; var clampedDirLength = new THREE.Vector3(); render(); function render() { let req = requestAnimationFrame(render); delta = clock.getDelta(); globalTime += delta; boxGeom.vertices.forEach(v => { clampedDirLength.copy(v.dir).multiplyScalar(globalTime * speed).clampLength(0, v.dist); // clamp the length! v.copy(v.random).add(clampedDirLength); }); boxGeom.verticesNeedUpdate = true; if (globalTime >= fullTime) { // Ссли тСкущая ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ большС ΠΈΠ»ΠΈ Ρ€Π°Π²Π½Π° Π·Π°Π΄Π°Π½Π½ΠΎΠΉ, Ρ‚ΠΎ останавливаСм Ρ†ΠΈΠΊΠ» прорисовки ΠΈΠ»ΠΈ Π΄Π΅Π»Π°Π΅ΠΌ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π΄Ρ€ΡƒΠ³ΠΎΠ΅ //cancelAnimationFrame(req); boxMat.color.set("aqua"); } renderer.render(scene, camera); } 
 body { overflow: hidden; margin: 0; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script> <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script> 

Reference

  • Thank. Thanks to the link left, another issue was resolved. - pepel_xD
  • @pepel_xD Not at all :) Glad to help. - prisoner849
  • the text did not follow the example of the link. Okay, I'll leave it for later. Anyway, the vector, the matrix .... still a dark forest for me ... I think you need to deal with it first, and then everything will be much clearer. - pepel_xD
  • @pepel_xD You can create another question here - suddenly you can help with it :) - prisoner849