系列文章目录
Three.js 快速入门教程【一】开启你的 3D Web 开发之旅
Three.js 快速入门教程【二】透视投影相机
Three.js 快速入门教程【三】渲染器
Three.js 快速入门教程【四】三维坐标系
Three.js 快速入门教程【五】动画渲染循环
Three.js 快速入门教程【六】相机控件 OrbitControls
Three.js 快速入门教程【七】常见几何体类型
Three.js 快速入门教程【八】常见材质类型
Three.js 快速入门教程【九】光源类型
文章目录
一、前言
在使用 Three.js 进行 3D 场景开发时,实现流畅的动画效果是至关重要的。而动画渲染循环则是实现动画的核心机制之一,其中 requestAnimationFrame 函数扮演着关键角色。如果你有看过该系列教程之前的文章对动画渲染循环一定不会陌生,经常出现在示例代码里面。本文将详细介绍 Three.js 中的动画渲染循环,并深入讲解 requestAnimationFrame 的工作原理和使用方法。
二、动画渲染循环的概念
动画渲染循环是指在每一帧中更新场景中的对象状态,并将更新后的场景渲染到屏幕上的过程。在 Three.js 中,我们通常使用一个循环来不断地调用渲染函数,以实现动画效果。然而,使用传统的 setInterval 或 setTimeout 来实现动画循环可能会导致性能问题和不流畅的动画效果,因为它们的执行时间间隔可能与屏幕的刷新率不一致。
三、requestAnimationFrame 详解
requestAnimationFrame 是浏览器提供的一个用于优化动画性能的 API。它的主要作用是告诉浏览器在下次重绘之前执行指定的函数,以更新动画。requestAnimationFrame 的优势在于:
1、与屏幕刷新率同步:requestAnimationFrame 的执行频率与屏幕的刷新率保持一致,这样可以确保动画的流畅性。常见的显示器刷新频率有60Hz、75Hz、120Hz、144Hz等
2、节能:当页面处于非活动状态时(例如切换到后台),requestAnimationFrame 会自动暂停,从而节省系统资源。
requestAnimationFrame 的使用方法如下:
function animate() {
// 更新场景中的对象状态
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;
// 渲染场景
renderer.render(scene, camera);
// 递归调用 animate 函数,实现动画循环
requestAnimationFrame(animate);
}
// 启动动画循环
animate();
在上述代码中,我们定义了一个 animate 函数,该函数在每一帧中更新物体的旋转角度,并调用 renderer.render 方法来渲染场景。然后,我们使用 requestAnimationFrame 递归调用 animate 函数,从而实现了一个连续的动画循环。
四、动画渲染循环在开发中应用
1、物体自转
绕Y轴方向自转
function animate() {
// 更新场景中的对象状态
mesh.rotation.y += 0.01;
// 渲染场景
renderer.render(scene, camera);
// 递归调用 animate 函数,实现动画循环
requestAnimationFrame(animate);
}
// 启动动画循环
animate();
2、物体绕坐标轴做圆周运动
通过修改物体rotation属性值只能让物体基于自身几何中心点自转,如果想实现物体围绕某个坐标轴旋转效果,就要控制物体位置使其在圆形轨道上做圆周运动。
以绕Y轴做圆周运动为例:
核心代码:
// 定义圆周运动的参数
const radius = 20; // 圆周的半径
let angle = 0; // 初始角度
const speed = 0.01; // 运动速度
// 渲染循环
function animate() {
requestAnimationFrame(animate);
// 计算物体在圆周上的位置
const x = radius * Math.cos(angle);
const z = radius * Math.sin(angle);
// 更新物体的位置
mesh.position.set(x, mesh.position.y, z);
// 增加角度,让物体继续运动
angle += speed;
renderer.render(scene, camera);
}
animate();
在 animate 函数中,使用 Math.cos 和 Math.sin 函数根据当前角度计算物体在 XZ 平面上的位置。
通过 mesh.position.set(x, mesh.position.y, z) 更新物体的位置。
每次循环增加 angle 的值,让物体继续沿着圆周运动。
完整代码:
import * as THREE from "three";
//引入相机控制器
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
//创建场景
const scene = new THREE.Scene();
scene.background='#000000'
// 创建坐标轴辅助器
const axesHelper = new THREE.AxesHelper(100);
scene.add(axesHelper);
//创建一个球体
const geometry = new THREE.SphereGeometry(3);
//创建一个基础材质
const material = new THREE.MeshBasicMaterial({ color: "#409EFF" });
//创建一个网格对象
const mesh = new THREE.Mesh(geometry, material);
//设置网格对象位置
mesh.position.set(0, 5, 20);
//添加到场景中
scene.add(mesh);
//创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
//设置相机位置
camera.position.set(0, 20, 50);
//相机默认看向网格对象
camera.lookAt(mesh.position);
//创建渲染器
const renderer = new THREE.WebGLRenderer();
//设置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比为设备的像素比
renderer.setPixelRatio(window.devicePixelRatio);
//将渲染器的 DOM 元素添加body
document.body.appendChild(renderer.domElement);
// 新建一个相机控件
const controls = new OrbitControls(camera, renderer.domElement);
// 启用阻尼(惯性效果)
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 定义圆周运动的参数
const radius = 20; // 圆周的半径
let angle = 0; // 初始角度
const speed = 0.01; // 运动速度
// 动画循环
function animate() {
// 计算物体在圆周上的位置
const x = radius * Math.cos(angle);
const z = radius * Math.sin(angle);
// 更新物体的位置
mesh.position.set(x, mesh.position.y, z);
// 增加角度,让物体继续运动
angle += speed;
// 定时刷新
requestAnimationFrame(animate);
// 更新控制器,必须调用以应用阻尼效果
controls.update();
//重新渲染
renderer.render(scene, camera);
}
// 执行动画
animate();
3、物体平移
/ 动画循环
function animate() {
//沿x轴正方向移动
mesh.position.x+=0.1
// 定时刷新
requestAnimationFrame(animate);
//重新渲染
renderer.render(scene, camera);
}
// 执行动画
animate();
4、物体缩放
//缩放比例
let scale = 1;
// 动画循环
function animate() {
scale = Math.max(0, scale - 0.002);
//缩放物体
mesh.scale.set(scale, scale, scale);
// 定时刷新
requestAnimationFrame(animate);
// 更新控制器,必须调用以应用阻尼效果
controls.update();
//重新渲染
renderer.render(scene, camera);
}
// 执行动画
animate();
五、总结
通过本文的介绍,我们了解了 Three.js 中动画渲染循环的基本概念,并深入学习了 requestAnimationFrame 的工作原理和使用方法。使用 requestAnimationFrame 可以帮助我们实现更加流畅和高效的动画效果,是 Three.js 开发中不可或缺的一部分
更多three.js入门知识点请关注该系列教程后续的更新。