Quiero que mi malla gire en ángulos de 90 grados en todas las direcciones, pero hacer que GSAP encuentre la "rotación óptima".
Entonces, si presiono 1 , estará en
this.meshName.rotation.y = 0 y 2
this.meshName.rotation.y = Math.PI/2 y 3
this.meshName.rotation.y = Math.PI y 4
this.meshName.rotation.y = (3*Math.PI)/2GSAP no encuentra la "rotación más óptima",
Por ejemplo, si presiono 4 y luego 1 , gira así (en rojo) 
y quiero que gire así (en verde)
\
La única solución en la que puedo pensar es hacer this.meshName.rotation.y = 2*Math.PI si la última tecla presionada fue 4 (y luego presiono 1 ), pero luego tengo que volver a configurarlo en 0 después de la animación. de lo contrario, afectará a las otras teclas. Lo que tengo ahora solo funciona a veces, pero es realmente complicado. Siento que hay una mejor manera de hacer esto .:
this.testLastKey = false; document.addEventListener('keydown', function(event) { if(event.key === "1") { if(this.testLastKey){ GSAP.to(this.mesh.rotation, { y: Math.PI*2, duration: 0.2, }); this.mesh.rotation = 0 } else { GSAP.to(this.mesh.rotation, { y: 0, duration: 0.2, }); } this.testLastKey = false } else if(event.key === "2") { GSAP.to(this.mesh.rotation, { y: Math.PI / 2, duration: 0.2, }); this.testLastKey = false } else if(event.key === "3") { GSAP.to(this.mesh.rotation, { y: Math.PI, duration: 0.2, }); this.testLastKey = false } else if(event.key === "4") { GSAP.to(this.mesh.rotation, { y: (3*Math.PI)/2, duration: 0.2, }); this.testLastKey = true } }Entonces, el problema principal aquí es que los ángulos de rotación regulares (Euler) no se interpolan bien cuando tienen que cruzar cero.
¡Cuaterniones al rescate!
Lo sé, lo sé, los cuaterniones son difíciles de entender. Pero no se preocupe, simplemente recurra a los métodos auxiliares de cuaterniones para hacer el trabajo pesado :-). Una vez que conviertes todos los ángulos en cuaterniones, se interpolan muy, muy bien.
El segundo problema es que GSAP no sabe que los valores de cuaterniones representan un ángulo, por lo que la interpolación es realmente rara. Hace el trabajo, pero el movimiento se ve realmente extraño. Lo mejor es usar los métodos de interpolación de cuaterniones de la biblioteca 3D (junto con GSAP). En este ejemplo se utiliza la interpolación lineal esférica (slerp)
(Implementación de ThreeJS)
//OBJECTIVE //User presses keys 1,2,3 or 4 which should rotate model's mesh to face 0,90, //180 or 270 degrees respectively (rotating on the y-axis) document.addEventListener("keypress", (event) => { //Taking advantage of integer keypress to simplify all rotation cases // into 1 case. For other cases like alphabet key presses you can create // a map of some sort const inKey = parseInt(event.key) - 1; //convert input key to 0 indexed 'iterator' const step = { factor: 0 }; //GSAP needs value set up as a property/object //prevent smart people from breaking your app with alphabet iterators :-) if (Number.isNaN(inKey)) return; //Create quaternion objects to store mesh's initial & final/target rotation. //Here, setFromEuler(x,y,z) converts a regular <x,y,z> rotation vector to // a quaternion <x,y,z,w> const initRot = new THREE.Quaternion().copy(mesh.quaternion); const targetRot = new THREE.Quaternion().setFromEuler( //Destination y-axis angle uses integer keypress input like an 'iterator' // current value, to make an implicit "map" for this exact use case/keypress. // Effectively 1=>0°,2=>90°,3=>180°,4=>270°,other input numbers modulo 4 new THREE.Euler(0, (inKey * Math.PI) / 2, 0) ); //GSAP tweening a number 'step.factor' from 0 to 1 GSAP.to(step, { factor: 1, //use this factor to interpolate "manually" duration: 0.2, //Fake "render loop" :-) Interpolate using threeJS slerpQuaternion // since GSAP doesn't know its an angle and interpolates awkwardly :-(. // This way we can run a custom interpolate on tick onUpdate: () => mesh.quaternion.slerpQuaternions(initRot, targetRot, step.factor), }); //end of GSAP.to }); //end of addEventListenerTL;DR. Aquí se explica cómo rotar la "malla" de un objeto 3D al presionar una tecla. (igual pero sin comentarios)
document.addEventListener("keypress", (event) => { const inKey = parseInt(event.key) - 1; const step = { factor: 0 }; if (Number.isNaN(inKey)) return; const initRot = new THREE.Quaternion().copy(mesh.quaternion); const targetRot = new THREE.Quaternion().setFromEuler( new THREE.Euler(0, (inKey * Math.PI) / 2, 0) ); GSAP.to(step, { factor: 1, duration: 0.2, onUpdate: () => mesh.quaternion.slerpQuaternions(initRot, targetRot, step.factor), }); });