如何让 GSAP 找到 "most optimal" 旋转方法?

How do I make GSAP find the "most optimal" rotation method?

我希望我的网格在所有方向上以 90 度角旋转,但让 GSAP 找到最“最佳旋转”。

所以如果我按 1 它会在

2

3

4

GSAP 没有找到“最优旋转”,

例如,如果我按4,然后按1,它会像这样旋转(红色)

我希望它像这样旋转(绿色) \

我能想到的唯一解决方案是,如果最后按下的键是 4(然后我按下 1),则制作 this.meshName.rotation.y = 2*Math.PI,但我必须将其设置回去在动画之后到 0 否则它会影响其他键。我现在所拥有的只是有时有效,但它真的很乱。我觉得有更好的方法来做到这一点。:

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

            }
        }

所以这里的主要问题是常规(欧拉)旋转角度在必须过零时不能很好地插值。

四元数来拯救!

我知道,我知道,四元数很难理解。但不用担心,只需调用四元数辅助方法来完成繁重的工作:-)。一旦将所有角度转换为四元数,它们的插值就会非常好。

第二个问题是GSAP不知道四元数值代表角度,所以插值真的很奇怪。它完成了工作,但动作看起来真的很奇怪。最好只使用 3D 库的四元数插值方法(连同 GSAP)。本例使用球面线性插值(slerp)


(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 addEventListener

长话短说;博士。以下是如何在按键时旋转 3D 对象“网格”。 (相同但无评论)

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),
  }); 
});