03 Threejs物体匀速运动

1,266 阅读5分钟

1. requestAnimationFrame请求动画帧

为了最好的利用性能和渲染效果,需要在绘制每一帧画面的时候,计算需要渲染的画面。也就是使用window.requestAnimationFrame方法。

1.1 requestAnimationFrame使用方式

window.requestAnimationFrame() 告诉浏览器——希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。

  • 该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。

  • 回调函数执行次数通常是每秒 60 次, 与浏览器屏幕刷新次数相匹配

function callback(){
  //下一帧渲染画面前,需要执行处理的函数
}
window.requestAnimationFrame(callback);

1.2 确保不同帧率的画面运行速度一致

回调函数第一个参数会被传入DOMHighResTimeStamp参数,DOMHighResTimeStamp是当前被 requestAnimationFrame() 排序的回调函数被触发的时间。

let preTime
function render(time) {
  //第一次调用render函数,没有上一帧的时间
  if (preTime === undefined) {
    preTime = time
  }

  //计算每帧画面的间隔时间,单位毫秒
  const deltaTime = time - preTime
  console.log('动画帧的间隔时间', deltaTime)
  //保留当前时间作为上一帧时间,用于下一帧计算2帧间隔
  preTime = time
  renderer.render(scene, camera)
  //   渲染下一帧的时候就会调用render函数
  requestAnimationFrame(render)
}

image.png

为了确保不同时间间隔,运动的速度一致,那么应该按照

移动距离 = 速度 * 时间

那么如果想要1m/s的速度远离原点的匀速运动

let preTime
function render(time) { 
 //requestAnimationFrame的回调函数默认传入一个时间参数
  console.log("当前总时间/毫秒", time)
  //第一次调用render函数,没有上一帧的时间
  if (preTime === undefined) {
    preTime = time
  }

  //计算每帧画面的间隔时间,单位毫秒,当前时间减去上一帧的时间,即为2帧直接的间隔时间
  const deltaTime = time - preTime || 0
  console.log('多少毫秒浏览器渲染一次', deltaTime)
  if (deltaTime) {
    console.log('浏览器1秒渲染的次数', 1000 / deltaTime)
  }

  preTime = time

  //cube物体允许运动
  //elapsedTime/1000是将毫秒改为秒
  //1m/s的速度* 时间(秒)= 移动的距离
  //将当前位置+=移动的距离,即为最后的距离
  cube.position.x += 1 * (deltaTime / 1000)
  if (cube.position.x > 3) {
    cube.position.x = 0
  }

  renderer.render(scene, camera)
  //   渲染下一帧的时候就会调用render函数
  requestAnimationFrame(render)
}

render()

image.png

1.3 匀速运动实例——使用requestAnimationFrame参数来获取时间,并处理动画

实现每3秒,即从原点出发匀速在x轴进行1m/s的匀速运动

1.gif

import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

// 目标:requestAnimationFrame 时间参数 控制物体动画 

// 1、创建场景
const scene = new THREE.Scene()

// 2、创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)

// 设置相机位置
camera.position.set(0, 0, 10)
scene.add(camera)

// 添加物体
// 创建几何体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1)
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })
// 根据几何体和材质创建物体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
// 将几何体添加到场景中
scene.add(cube)
console.log(cube)

// 初始化渲染器
const renderer = new THREE.WebGLRenderer()
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight)
// console.log(renderer);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement)

// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)
// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(3)
scene.add(axesHelper)

function render(time) {
  let t = (time / 1000) % 3
  cube.position.x = t * 1

  renderer.render(scene, camera)
  //   渲染下一帧的时候就会调用render函数
  requestAnimationFrame(render)
}

render()

2. Clock对象——跟踪时间,处理动画

使用three.js自带的Clock类实例的对象来完成时间的处理

2.1 Clock

该对象用于跟踪时间。

// 实例化clock对象,
// new Clock( autoStart : Boolean ),
// autoStart — (可选) 是否要在第一次调用 .getDelta() 时自动开启时钟。默认值是 true。
// 初始化时钟
const clock = new THREE.Clock();

image.png

image.png

2.2 获取运行当前帧的时间

2.2.1 getElapsedTime ()获取自时钟启动后的秒数。

// 设置时钟
const clock = new THREE.Clock();
function render() {
  // 获取时钟运行的总时长
  let time = clock.getElapsedTime();
  console.log("时钟运行总时长:", time);

  renderer.render(scene, camera);
  //   渲染下一帧的时候就会调用render函数
  requestAnimationFrame(render);
}

render();

1.gif

2.2.2 getDelta () 获取2帧之间的时间间隔。

// 设置时钟
const clock = new THREE.Clock();
function render() {
  // 获取2帧之间的时间间隔
  let deltaTime = clock.getDelta();
  console.log("两帧之间的时间间隔:", deltaTime);

  renderer.render(scene, camera);
  //   渲染下一帧的时候就会调用render函数
  requestAnimationFrame(render);
}

render();

1.gif

注意:getDelta、getElapsedTime如果同时用于同一动画帧,先调用getElapsedTime()会导致getDelta计时不准。因为每次调用这2个函数,都会对oldTime属性进行重置,所以getDelta计算出来的就不是上一帧的时间。

image.png

2.3 Clock匀速运动实例——使用getElapsedTime()来获取时间,并处理动画

实现每3秒,即从原点出发匀速在x轴进行1m/s的匀速运动

1.gif

import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

// 目标:控制3d物体旋转

// 1、创建场景
const scene = new THREE.Scene()

// 2、创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)

// 设置相机位置
camera.position.set(0, 0, 10)
scene.add(camera)

// 添加物体
// 创建几何体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1)
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })
// 根据几何体和材质创建物体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
// 将几何体添加到场景中
scene.add(cube)
console.log(cube)

// 初始化渲染器
const renderer = new THREE.WebGLRenderer()
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight)
// console.log(renderer);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement)

// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)
// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(3)
scene.add(axesHelper)
const clock = new THREE.Clock()
function render(time) {
  let deltaTime = clock.getDelta()
  console.log('两次获取时间的间隔时间:', deltaTime)
  let totalTime = clock.getElapsedTime()
  console.log('时钟运行总时长:', totalTime)

  let t = totalTime % 3
  cube.position.x = t * 1
  renderer.render(scene, camera)
  //   渲染下一帧的时候就会调用render函数
  requestAnimationFrame(render)
}

render()