You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化Three.js多元素场景:不同动画与色彩的性能优化

多Blob动画的Three.js性能优化方案

核心思路

放弃为每个.blob元素创建独立的Scene、Renderer,改为全局单一场景+渲染器,所有Blob作为独立Mesh存在于同一环境中,复用灯光、相机等资源,通过统一渲染循环驱动所有动画,彻底解决重复资源创建导致的性能问题。

具体实现步骤

  1. 全局资源初始化:只创建一次Scene、WebGLRenderer、相机和灯光,确保灯光配置能覆盖所有Mesh的光照需求。
  2. Blob实例化:为每个DOM元素创建独立的Mesh,通过不同材质实现差异化色彩,自定义userData存储动画参数实现差异化动画。
  3. DOM位置绑定:监听页面滚动、窗口 resize 事件,实时将DOM元素的屏幕坐标转换为Three.js世界坐标,更新对应Mesh的位置。
  4. 统一渲染循环:用单个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:05:15