本文使用Three.js和GSAP库创建一个包含 3x3x3 立方体网格的三维动画。
1. 引入所需库
在开始之前,我们需要引入 Three.js 和 GSAP:
- Three.js:用于渲染 3D 图形的核心库。
- GSAP:用于动画控制,能够让我们轻松实现动画效果。
import * as THREE from "three";
import gsap from "gsap";
2. 创建 Three.js 场景
为了创建 3D 动画效果,我们需要设置 场景、相机、渲染器、光源、地面 以及 立方体网格。
2.1 设置相机
我们使用 正交相机,这样物体的大小不会因距离而改变,并将相机放置在 (500, 500, 500)
,朝向场景中心。
const setupCamera = () => {
camera = new THREE.OrthographicCamera(
window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
1,
1000
);
camera.position.set(500, 500, 500);
camera.lookAt(0, 0, 0);
};
2.2 设置渲染器
我们使用 WebGLRenderer
进行渲染,并启用抗锯齿和阴影。
const setupRenderer = () => {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xf9f8ed, 1);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
};
2.3 设置光源
为了让立方体产生阴影,我们添加三种光源:
- 主光源:用于产生阴影。
- 正面补光:增强可见度。
- 背面补光:改善立体感。
const setupLight = () => {
const shadowlight = new THREE.DirectionalLight(0xffffff, 1.8);
shadowlight.position.set(0, 50, 0);
shadowlight.castShadow = true;
scene.add(shadowlight);
const light = new THREE.DirectionalLight(0xffffff, 1.8);
light.position.set(60, 100, 20);
scene.add(light);
const backLight = new THREE.DirectionalLight(0xffffff, 1);
backLight.position.set(-40, 100, 20);
scene.add(backLight);
};
2.4 创建地面
我们添加一个 白色平面 作为地面,使立方体投下阴影。
const setupFloor = () => {
const floorGeometry = new THREE.PlaneGeometry(500, 500);
const floorMaterial = new THREE.MeshBasicMaterial({ color: 0xf9f8ed });
floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.set(0, -200, 0);
floor.rotation.x = Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
};
3. 创建立方体网格
3.1 生成 3x3x3 立方体
接下来,我们使用 THREE.Group()
创建 3x3x3 的立方体网格。每个立方体大小为 50x50x50,间隔 5px。
const initShape = () => {
myArray = new THREE.Group();
scene.add(myArray);
let lx = 0,
ly = 0,
lz = 0;
for (let i = 0; i < 27; i++) {
geometry = new THREE.BoxGeometry(50, 50, 50);
material = new THREE.MeshLambertMaterial({ color: 0x202020 });
shape = new THREE.Mesh(geometry, material);
shape.castShadow = true;
if (lx % 3 == 0) {
lx = 0;
if (ly % 3 == 0) {
lz += 1;
ly = 0;
}
ly += 1;
}
shape.position.set(lx * 55, ly * 55, lz * 55);
myArray.add(shape);
lx += 1;
}
};
4. 添加动画
4.1 使用 GSAP 实现缩放动画
我们利用 GSAP 为每个立方体添加 缩放动画,实现不断放大和缩小的效果。
const animateShapes = () => {
myArray.children.forEach((shape, index) => {
const delay = (index % 3) * 0.1;
gsap
.timeline({ repeat: -1, repeatDelay: 0.5, delay })
.to(shape.scale, { duration: 0.7, x: 0, y: 0, z: 0 })
.to(shape.scale, { duration: 0.7, x: 1, y: 1, z: 1 });
});
};
5. 渲染与动画循环
我们使用 requestAnimationFrame
让动画不断执行,并渲染场景。
const render = () => {
requestAnimationFrame(render);
renderer.render(scene, camera);
};
6. 初始化整个场景
我们将所有步骤组合到 init()
函数中,并启动 Three.js 场景。
const init = () => {
setupCamera();
setupRenderer();
setupLight();
setupFloor();
initShape();
animateShapes();
render();
};
最后,我们调用 init()
来运行整个项目。
init();
完整代码
/**
* 引入必要的三维渲染引擎和动画库
*/
import * as THREE from "three";
import gsap from "gsap";
/**
* 创建三维场景的工厂函数
* @returns {Object} 返回包含初始化方法的对象
*/
const createThreeJS = () => {
// 场景基础变量定义
const scene = new THREE.Scene(); // 创建场景实例
let camera = null; // 相机实例
let renderer = null; // 渲染器实例
let floor = null; // 地面网格
let myArray = null; // 立方体组
let shape = null; // 单个立方体网格
let geometry = null; // 几何体
let material = null; // 材质
let tl = null; // 动画时间轴
/**
* 设置正交相机
* 用于创建一个不具有透视效果的相机,物体大小不会随距离改变
*/
const setupCamera = () => {
camera = new THREE.OrthographicCamera(
window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
1,
1000
);
camera.position.y = 500;
camera.position.z = 500;
camera.position.x = 500;
camera.updateProjectionMatrix();
camera.lookAt(scene.position);
};
/**
* 设置渲染器
* 配置WebGL渲染器的基本属性,包括抗锯齿、阴影等
*/
const setupRenderer = () => {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xf9f8ed, 1);
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
};
/**
* 设置场景光源
* 添加三种不同位置的平行光,创建立体感
*/
const setupLight = () => {
// 主阴影光源
const shadowlight = new THREE.DirectionalLight(0xffffff, 1.8);
shadowlight.position.set(0, 50, 0);
shadowlight.castShadow = true;
shadowlight.shadowDarkness = 0.1;
scene.add(shadowlight);
// 正面补光
const light = new THREE.DirectionalLight(0xffffff, 1.8);
light.position.set(60, 100, 20);
scene.add(light);
// 背面补光
const backLight = new THREE.DirectionalLight(0xffffff, 1);
backLight.position.set(-40, 100, 20);
scene.add(backLight);
};
/**
* 创建地面
* 添加一个接收阴影的平面作为地面
*/
const setupFloor = () => {
const floorGeometry = new THREE.PlaneGeometry(500, 500, 1, 1);
const floorMaterial = new THREE.MeshBasicMaterial({ color: 0xf9f8ed });
floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.material.side = THREE.DoubleSide;
floor.position.y = -200;
floor.position.z = -100;
floor.rotation.x = (90 * Math.PI) / 180;
floor.rotation.y = 0;
floor.rotation.z = 0;
floor.doubleSided = true;
floor.receiveShadow = true;
scene.add(floor);
};
/**
* 初始化立方体组
* 创建3x3x3的立方体网格,并添加缩放动画
*/
const initShape = () => {
myArray = new THREE.Group();
scene.add(myArray);
let lx = 0; // x轴位置计数器
let ly = 0; // y轴位置计数器
let lz = 0; // z轴位置计数器
// 创建27个立方体(3x3x3)
for (let i = 0; i < 27; i++) {
// 创建立方体几何体和材质
geometry = new THREE.BoxGeometry(50, 50, 50);
material = new THREE.MeshLambertMaterial({
color: 0x202020,
shading: THREE.FlatShading,
});
shape = new THREE.Mesh(geometry, material);
shape.castShadow = true;
shape.receiveShadow = false;
// 计算立方体位置
if (lx % 3 == 0) {
lx = 0;
if (ly % 3 == 0) {
lz += 1;
ly = 0;
}
ly += 1;
}
// 设置立方体位置
shape.position.x = lx * 55;
shape.position.y = ly * 55;
shape.position.z = lz * 55;
myArray.add(shape);
// 创建缩放动画
tl = gsap.timeline({
repeat: -1, // 无限循环
repeatDelay: 0.5, // 循环间隔1秒
delay: ly * 0.05, // 基于y轴位置的延迟
});
// 添加缩放动画序列
tl.to(shape.scale, {
duration: 0.7,
x: 0,
y: 0,
z: 0, // 缩放到0
ease: "expo.out",
}).to(shape.scale, {
duration: 0.7,
x: 1,
y: 1,
z: 1, // 恢复原始大小
ease: "expo.out",
});
lx += 1;
}
};
/**
* 渲染函数
* 创建动画循环,持续渲染场景
*/
const render = () => {
requestAnimationFrame(render);
renderer.render(scene, camera);
};
/**
* 初始化函数
* 按顺序执行所有设置函数
*/
const init = () => {
setupCamera(); // 设置相机
setupRenderer(); // 设置渲染器
setupLight(); // 设置光源
setupFloor(); // 设置地面
initShape(); // 初始化立方体
render(); // 开始渲染
};
// 只暴露初始化方法
return {
init,
};
};
// 创建实例并初始化
const threeJS = createThreeJS();
threeJS.init();