简单的 3D 立方体滚动动画不适用于 Threejs [英] Simple 3D cube roll animations not working with threejs

查看:29
本文介绍了简单的 3D 立方体滚动动画不适用于 Threejs的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 THREEJS 的新手,目前我正在尝试使用箭头键移动一个立方体.请参阅此小提琴:https://jsfiddle.net/mauricederegt/y6cw7foj/26/

I am new to THREEJS and at the moment I am trying to move a cube using arrow keys. Please see this fiddle: https://jsfiddle.net/mauricederegt/y6cw7foj/26/

一切正常,我可以使用箭头键移动立方体,我什至在移动立方体时设法围绕正确的轴旋转立方体.问题在于动画.我似乎无法让他们工作.在您按下向左箭头键的那一刻,立方体向左移动并绕轴滚动.嗯……目前它会卡入到位,而不是平滑过渡.

All works, I can move the cube using the arrow keys and I even managed to rotate the cube around the correct axis when moving it around. The problem is with the animations. I can’t seem to get them to work. At the moment when you press the left arrow key, the cube moves to the left and also rolls around the axis. Well…at the moment it snaps into position, instead of smoothly transitioning.

我想要的是它在旋转时平滑地向左移动,但如何做到这一点?在代码的末尾,我确实调用了

What I want is that it smoothly moves to the left while it rotates, but how to do that? At the end of the code I do call for the

requestAnimationFrame

但这并没有多大作用.我在这里尝试使用 CSS 来做这件事.这里的动画工作(但从来没有正确的旋转方向):https://jsfiddle.net/mauricederegt/5ozqg9uL/3/ 这确实显示了我想要的动画.

but that doesn’t do much. I have a fiddle here of my attempt doing this with CSS. Here the animations work (but never got the rotating direction correct): https://jsfiddle.net/mauricederegt/5ozqg9uL/3/ This does show what animations I want to have.

那么我在 THREEjs 中缺少什么?非常感谢

So what am I missing in the THREEjs? Thanks a lot

推荐答案

您正在寻找的是一种叫做补间"的东西.在那里您绘制中间步骤,而不是立即跳转到最终结果.有几个 JavaScript 库可以为您执行此操作,但我将介绍自己实现它的一些基础知识.

What you're looking for is something called "tweening," where you draw intermediary steps, rather than jumping immediately to the end result. There are several JavaScript libraries that will do this for you, but I'll cover some of the basics of implementing it yourself.

举个例子.当您点击左箭头时,您将沿 -x 轴移动网格 1 单元,并围绕 旋转 -PI/2>+y 轴.与其捕捉到这些位置/旋转,不如考虑您希望动画持续多长时间,然后开始划分步骤.

Take your example. When you tap the left arrow, you move the mesh 1 unit along the -x axis, and rotate -PI/2 about the +y axis. Rather than snapping to these positions/rotations, consider how long you want the animation to take, and start dividing out steps.

假设您想要 500 毫秒(半秒).您的浏览器尝试以大约 60fps 运行,因此您有 30 帧(大约 500ms)以该速率运行.因此,对于每一帧,您可以将框移动 1/30 个单位,并将其旋转 -PI/60.在 30 帧之后,框应该在正确的位置,进行一些四舍五入.

Let's say you want to to take 500ms (half a second). Your browser tries to run at about 60fps, so you have 30 frames (about 500ms) to work with at that rate. So for every frame, you can move the box 1/30 units, and rotate it by -PI/60. After 30 frames, the box should be in about the right place, give or take some rounding.

我使用关于"在谈论浏览器的帧率时,因为您并不总能保证获得 60FPS.如果您的帧率下降,并且您被锁定在帧率来绘制动画,那么它也会变慢并花费比您想要的更长的时间.那么可以做些什么呢?

I use "about" when talking about the framerate of the browser because you aren't always guaranteed to get 60FPS. If your framerates dip, and you're locked to the framerate to draw your animation, then it too will slow down and take longer than you wanted. So what can be done about that?

与其依赖 requestAnimationFrame 作为您的计时器,您还可以设置一个 real 计时器来逐步执行您的动画.折腾计算完成动画所需的帧数,而是计算所需的步骤.

Rather than relying on requestAnimationFrame as your timer, you can set a real timer to step through your animation. Toss calculating the frames you need to complete the animation, and instead calculate the steps needed.

我们已经知道60fps大约是每16.6ms1帧,所以这是浏览器可以期望绘制的绝对最大目标.但是,当我们逐步进行更新时,没有什么能阻止我们加快速度.为了使计算更容易,假设我们要执行 50 个更新步骤,而不是之前的 30.这意味着对于 500ms 播放时间,我们需要每 10ms 执行一次更新(比帧速率略快).另外,因为我们要执行 50 步,所以我们将以 1/50 为单位更新位置,并以 -PI/100 为单位旋转.

We already know that 60fps is roughly 1 frame every 16.6ms, so that's the absolute maximum target that can expect the browser to draw. But when we do our updates by steps, nothing stops us from going faster. To make things easier to calculate, let's say we want to do 50 update steps rather than the 30 from before. This means that for the 500ms play time, we will need to perform an update every 10ms (slightly faster than the framerate). Also, because we are performing 50 steps, we will be updating the position by 1/50 units, and rotating by -PI/100.

let animationId = setInterval( ()=>{
  // update position by 1/50 units
  // update rotation by -PI/100
}, 10 ); // every 10 ms

随着时间间隔的运行,它将更新对象.同时,动画循环会尽可能地生成新帧.

As the interval runs, it will update the object. Meanwhile, the animation loop churns out new frames whenever it can.

这是一个完整的运行示例,仅支持左箭头:

Here's a full, running example, with only left-arrow support:

let W = window.innerWidth;
let H = window.innerHeight;

const renderer = new THREE.WebGLRenderer({
  antialias: true,
  alpha: true
});
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(28, 1, 1, 1000);
camera.position.set(0, 0, 50);
camera.lookAt(scene.position);
scene.add(camera);

const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 0, -1);
camera.add(light);

const cube = new THREE.Mesh(
  new THREE.BoxBufferGeometry(1, 1, 1),
  new THREE.MeshPhongMaterial({
    color: "red"
  })
);
cube.position.set(10, 0, 0);
scene.add(cube);

function render() {
  renderer.render(scene, camera);
}

function resize() {
  W = window.innerWidth;
  H = window.innerHeight;
  renderer.setSize(W, H);
  camera.aspect = W / H;
  camera.updateProjectionMatrix();
  render();
}

window.addEventListener("resize", resize);

resize();

function animate() {
  requestAnimationFrame(animate);
  render();
}
requestAnimationFrame(animate);

const yAxis = new THREE.Vector3(0, 1, 0);

function updateCube() {
  //cube.position.x -= 1;
  //cube.rotateOnWorldAxis(yAxis, THREE.Math.degToRad(-90));
  cube.position.x -= 1 / 50;
  cube.rotateOnWorldAxis(yAxis, -(Math.PI / 100));
}

let step = 0;
let animationId = null;

function startStepping() {
  animationId = setInterval(() => {
    updateCube();
    if (++step === 50) {
      clearInterval(animationId);
      animationId = null;
    }
  }, 10)
}

function handleKeyboard(e) {
  //if (e.keyCode == 65 || e.keyCode == 37) {
  //  updateCube();
  //}
  if (animationId === null && (e.keyCode == 65 || e.keyCode == 37)) {
    step = 0;
    startStepping();
  }
}
document.addEventListener("keydown", handleKeyboard, false);

html,
body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  overflow: hidden;
  background: skyblue;
}

<script src="https://threejs.org/build/three.min.js"></script>

现在这种方法有一个缺点.有时您可能会看到更新被跳过(如果浏览器的帧率下降),或者同一位置绘制了两次(如果您的更新率明显低于浏览器的帧率).您可以尝试两全其美,但要从渲染循环中实时计算帧率,并相应地调整帧步长,但此时您必须问,计算这些统计数据所花费的额外时间是否真的会伤害尝试实现摇滚- 稳定的帧率锁定绘制率.

Now there is a downside to this method. Sometimes you may see updates skipped (if the browser's framerate drops), or the same position drawn twice (if your update rate is significantly lower than the browser's framerate). You could try to get the best of both worlds but computing the framerate live from your render loop, and adjusting your frame steps accordingly, but at that point you must ask whether the extra time spent computing those statistics are actually hurting trying to achieve a rock-steady framerate-locked draw rate.

由于您的键输入现在与绘图脱节,您现在需要某种标志来确定正在执行的操作.您的按键处理程序将设置该标志,然后 updateCube 将根据该标志执行操作.类似的东西:

Because your key input is now disjointed from drawing, you now need some kind of flag to determine the action being taken. Your key press handler will set that flag, and then updateCube will act based on that flag. Something like:

let action = null

function startStepping(){
  // set up the interval...
  // but then also ensure the action stops after the animation plays:
  setTimeout( () => action = null, 500 );
}

function handleKeyboard(e){
  if (animationId === null) {
    step = 0;

    switch(e.keyCode){
      case 37:
      case 65:
        action = "left";
        break;
      // other keys...
    }

    startStepping();
  }
}

function updateCube(){
  switch(action){
    case "left":
      // move as if it's rolling left
      break;
    case "right":
      // move as if it's rolling right
      break;
    // etc. for the other directions
  }
}

这篇关于简单的 3D 立方体滚动动画不适用于 Threejs的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆