优化Three.js多元素场景:不同动画与色彩的性能优化
多Blob动画的Three.js性能优化方案
核心思路
放弃为每个.blob元素创建独立的Scene、Renderer,改为全局单一场景+渲染器,所有Blob作为独立Mesh存在于同一环境中,复用灯光、相机等资源,通过统一渲染循环驱动所有动画,彻底解决重复资源创建导致的性能问题。
具体实现步骤
- 全局资源初始化:只创建一次
Scene、WebGLRenderer、相机和灯光,确保灯光配置能覆盖所有Mesh的光照需求。 - Blob实例化:为每个DOM元素创建独立的
Mesh,通过不同材质实现差异化色彩,自定义userData存储动画参数实现差异化动画。 - DOM位置绑定:监听页面滚动、窗口 resize 事件,实时将DOM元素的屏幕坐标转换为Three.js世界坐标,更新对应Mesh的位置。
- 统一渲染循环:用单个
requestAnimationFrame处理所有Blob的动画逻辑与场景渲染。
关键代码示例
1. 全局资源初始化(解决灯光颜色不显示问题)
// 全局场景、渲染器、正交相机(适配DOM元素位置绑定) const scene = new THREE.Scene(); const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); const camera = new THREE.OrthographicCamera( window.innerWidth / -2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / -2, 1, 1000 ); camera.position.z = 500; // 全局灯光组合:环境光+点光,确保所有Mesh能正确接收光照 const ambientLight = new THREE.AmbientLight(0x404040, 0.6); scene.add(ambientLight); const pointLight = new THREE.PointLight(0xffffff, 1); pointLight.position.set(0, 0, 300); scene.add(pointLight);
2. 创建差异化Blob
// 复用基础几何体(减少内存占用) const baseGeometry = new THREE.SphereGeometry(50, 32, 32); function createBlob(colorHex, animationSpeed) { // 每个Blob用独立材质实现色彩差异化 const material = new THREE.MeshPhongMaterial({ color: colorHex, transparent: true, opacity: 0.7, shininess: 30 }); const mesh = new THREE.Mesh(baseGeometry, material); // 存储动画参数,用于差异化动画逻辑 mesh.userData = { time: 0, speed: animationSpeed }; scene.add(mesh); return mesh; }
3. 绑定DOM元素与Mesh位置
const blobMap = []; // 初始化所有Blob document.querySelectorAll('.blob').forEach(el => { // 随机生成差异化色彩与动画速度 const randomColor = Math.random() * 0xffffff; const randomSpeed = 0.01 + Math.random() * 0.02; const mesh = createBlob(randomColor, randomSpeed); blobMap.push({ el, mesh }); }); // 更新Mesh位置:将DOM元素屏幕坐标转为Three.js世界坐标 function syncBlobPositions() { blobMap.forEach(({ el, mesh }) => { const rect = el.getBoundingClientRect(); const x = rect.left + rect.width / 2 - window.innerWidth / 2; const y = -(rect.top + rect.height / 2 - window.innerHeight / 2); // 正交相机Y轴反向 mesh.position.set(x, y, 0); }); } // 监听滚动与窗口大小变化,同步位置 window.addEventListener('scroll', syncBlobPositions); window.addEventListener('resize', () => { // 更新相机投影矩阵 camera.left = window.innerWidth / -2; camera.right = window.innerWidth / 2; camera.top = window.innerHeight / 2; camera.bottom = window.innerHeight / -2; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); syncBlobPositions(); }); // 初始化位置同步 syncBlobPositions();
4. 统一渲染与动画循环
function animate() { requestAnimationFrame(animate); // 执行每个Blob的差异化动画 blobMap.forEach(({ mesh }) => { mesh.userData.time += mesh.userData.speed; const positions = mesh.geometry.attributes.position.array; // 顶点扰动实现Blob变形动画 for (let i = 0; i < positions.length; i += 3) { const noise = Math.sin(mesh.userData.time + positions[i] * 0.01 + positions[i+1] * 0.01) * 5; positions[i+2] = positions[i+2] + noise; } mesh.geometry.attributes.position.needsUpdate = true; mesh.geometry.computeVertexNormals(); // 确保光照正常更新 }); renderer.render(scene, camera); } animate();
关键问题解决
- 灯光移到循环外不显示颜色:之前大概率用了
MeshBasicMaterial(不响应光照),换成MeshPhongMaterial/MeshStandardMaterial,同时搭配环境光+点光的组合,确保所有Mesh能正确接收光照并显示自身颜色。 - 动态添加/移除Blob:如果页面长度变化需要新增/删除Blob,只需调用
createBlob添加到场景,或调用scene.remove(mesh)移除即可,无需重新初始化整个Three.js环境。
内容的提问来源于stack exchange,提问作者Igor Mitic
相关产品推荐
相关产品推荐

