1. 基础窗口适配

1.1. 初始化设置

javascript

this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(
  75, // 视场角
  window.innerWidth / window.innerHeight, // 初始宽高比
  0.1, // 近裁剪面
  1000 // 远裁剪面
);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
// 设置像素比,适配高DPI屏幕
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

// 设置渲染器尺寸
this.renderer.setSize(window.innerWidth, window.innerHeight);

// 启用阴影(如果需要)
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;

// 将渲染器添加到DOM
document.body.appendChild(this.renderer.domElement);

2. 响应式事件处理

2.1. 窗口大小变化监听

javascript

// 监听窗口大小变化
window.addEventListener('resize', onWindowResize);

// 监听设备方向变化(移动端)
window.addEventListener('orientationchange', onWindowResize);

// 监听全屏变化
document.addEventListener('fullscreenchange', onFullscreenChange);

function onWindowResize() {
    // 更新相机宽高比
    this.camera.aspect = window.innerWidth / window.innerHeight;
    this.camera.updateProjectionMatrix();

    // 更新渲染器尺寸
    this.renderer.setSize(window.innerWidth, window.innerHeight);

    // 更新后处理效果(如果使用)
    if (this.effectComposer) {
      this.effectComposer.setSize(window.innerWidth, window.innerHeight);
    }
}
function onFullscreenChange() {
    // 延迟执行以确保获取正确的尺寸
    setTimeout(() => this.onWindowResize(), 100);
}

3. 高级响应式策略

3.1. 自适应相机

javascript

class ResponsiveCamera {
  constructor(camera, options = {}) {
    this.camera = camera;
    this.options = {
      minFov: 30,
      maxFov: 75,
      breakpoints: {
        mobile: 768,
        tablet: 1024,
        desktop: 1200
      },
      ...options
    };

    this.updateCameraSettings();
  }

  updateCameraSettings() {
    const width = window.innerWidth;
    const height = window.innerHeight;

    // 根据屏幕尺寸调整视场角
    if (width < this.options.breakpoints.mobile) {
      // 移动端使用更大的视场角
      this.camera.fov = this.options.maxFov;
    } else if (width < this.options.breakpoints.tablet) {
      // 平板端
      this.camera.fov = 65;
    } else {
      // 桌面端
      this.camera.fov = this.options.minFov;
    }

    // 处理超宽屏
    if (width / height > 2) {
      this.camera.fov = Math.max(this.options.minFov, this.camera.fov * 0.8);
    }

    this.camera.updateProjectionMatrix();
  }
}

3.2. 响应式场景内容

通过配置桌面端和移动端的数据,让场景特定内容根据配置信息能够在不同的设备上更好的展示。
javascript

class ResponsiveScene {
  constructor(scene, camera) {
    this.scene = scene;
    this.camera = camera;
    this.objects = new Map(); // 存储需要响应的对象
  }

  addResponsiveObject(object, config) {
    this.objects.set(object.uuid, {
      object,
      config: {
        scale: config.scale || 1,
        position: config.position || { x: 0, y: 0, z: 0 },
        mobile: config.mobile || {},
        desktop: config.desktop || {}
      }
    });
  }

  updateForScreenSize() {
    const width = window.innerWidth;
    const height = window.innerHeight;
    const isMobile = width < 768;

    this.objects.forEach((data) => {
      const { object, config } = data;

      if (isMobile && config.mobile) {
        // 移动端配置
        if (config.mobile.scale) {
          object.scale.setScalar(config.mobile.scale);
        }
        if (config.mobile.position) {
          object.position.set(
            config.mobile.position.x,
            config.mobile.position.y,
            config.mobile.position.z
          );
        }
      } else if (!isMobile && config.desktop) {
        // 桌面端配置
        if (config.desktop.scale) {
          object.scale.setScalar(config.desktop.scale);
        }
        if (config.desktop.position) {
          object.position.set(
            config.desktop.position.x,
            config.desktop.position.y,
            config.desktop.position.z
          );
        }
      }
    });
  }
}

4. 性能优化的响应式

4.1. 根据设备性能调整设置

javascript

class AdaptiveRenderer {
  constructor(renderer) {
    this.renderer = renderer;
    this.detectDeviceCapabilities();
    this.applyOptimalSettings();
  }

  detectDeviceCapabilities() {
    this.capabilities = {
      isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
        navigator.userAgent
      ),
      pixelRatio: window.devicePixelRatio,
      memory: navigator.deviceMemory || 4,
      cores: navigator.hardwareConcurrency || 4
    };
  }

  applyOptimalSettings() {
    const { isMobile, pixelRatio, memory } = this.capabilities;

    // 设置像素比
    const maxPixelRatio = isMobile ? 1.5 : 2;
    this.renderer.setPixelRatio(Math.min(pixelRatio, maxPixelRatio));

    // 根据设备内存调整阴影质量
    if (memory < 4) {
      this.renderer.shadowMap.type = THREE.BasicShadowMap;
    } else if (memory < 8) {
      this.renderer.shadowMap.type = THREE.PCFShadowMap;
    } else {
      this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    }

    // 移动端禁用抗锯齿以提升性能
    if (isMobile) {
      this.renderer.domElement.getContext('webgl2')?.getContextAttributes().antialias = false;
    }
  }

  adjustForPerformance(fps) {
    // 根据当前FPS动态调整质量
    if (fps < 30) {
      // 降低质量
      this.renderer.shadowMap.enabled = false;
      if (this.postProcessing) {
        this.postProcessing.enabled = false;
      }
    } else if (fps > 50) {
      // 提高质量
      this.renderer.shadowMap.enabled = true;
      if (this.postProcessing) {
        this.postProcessing.enabled = true;
      }
    }
  }
}

5. 完整的响应式 Three.js 应用

javascript

class ResponsiveThreeApp {
  constructor() {
    this.init();
    this.setupResponsiveSystems();
    this.animate();
  }

  init() {
    // 基础初始化
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );
    this.camera.position.z = 5;

    this.renderer = new THREE.WebGLRenderer({
      antialias: true,
      alpha: true,
      powerPreference: 'high-performance'
    });

    // 响应式初始化
    this.responsiveCamera = new ResponsiveCamera(this.camera);
    this.responsiveScene = new ResponsiveScene(this.scene, this.camera);
    this.adaptiveRenderer = new AdaptiveRenderer(this.renderer);

    this.setupRenderer();
    this.setupEventListeners();
    this.createResponsiveContent();
  }

  setupRenderer() {
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

    // 启用色调映射
    this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
    this.renderer.toneMappingExposure = 1;

    // 添加到页面
    const container = document.getElementById('three-container') || document.body;
    container.appendChild(this.renderer.domElement);

    // 设置画布样式
    this.renderer.domElement.style.position = 'fixed';
    this.renderer.domElement.style.top = '0';
    this.renderer.domElement.style.left = '0';
    this.renderer.domElement.style.width = '100%';
    this.renderer.domElement.style.height = '100%';
    this.renderer.domElement.style.zIndex = '0';
  }

  setupEventListeners() {
    // 防抖的resize处理
    let resizeTimeout;
    window.addEventListener('resize', () => {
      clearTimeout(resizeTimeout);
      resizeTimeout = setTimeout(() => this.handleResize(), 250);
    });

    // 监听可见性变化
    document.addEventListener('visibilitychange', () => {
      if (document.hidden) {
        this.pause();
      } else {
        this.resume();
      }
    });
  }

  handleResize() {
    const width = window.innerWidth;
    const height = window.innerHeight;

    // 更新相机
    this.camera.aspect = width / height;
    this.responsiveCamera.updateCameraSettings();

    // 更新渲染器
    this.renderer.setSize(width, height);
    this.adaptiveRenderer.applyOptimalSettings();

    // 更新场景内容
    this.responsiveScene.updateForScreenSize();

    // 更新后处理(如果使用)
    if (this.effectComposer) {
      this.effectComposer.setSize(width, height);
    }

    console.log(`Resized to: ${width}x${height}`);
  }

  createResponsiveContent() {
    // 创建响应式3D物体示例
    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
    const cube = new THREE.Mesh(geometry, material);
    this.scene.add(cube);

    // 配置不同屏幕尺寸下的显示方式
    this.responsiveScene.addResponsiveObject(cube, {
      mobile: {
        scale: 0.5,
        position: { x: 0, y: 1, z: 0 }
      },
      desktop: {
        scale: 1,
        position: { x: 0, y: 0, z: 0 }
      }
    });
  }

  animate() {
    requestAnimationFrame(() => this.animate());

    // 渲染场景
    this.renderer.render(this.scene, this.camera);

    // 性能监控和适配
    if (this.performanceMonitor) {
      const fps = this.performanceMonitor.getFPS();
      this.adaptiveRenderer.adjustForPerformance(fps);
    }
  }

  pause() {
    // 暂停渲染循环以节省资源
    cancelAnimationFrame(this.animationId);
  }

  resume() {
    this.animate();
  }
}

// 启动应用
const app = new ResponsiveThreeApp();

6. 6. CSS 配合方案

css

/* 确保Three.js画布正确显示 */
#three-container {
  position: relative;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}

/* 移动端优化 */
@media (max-width: 768px) {
  #three-container canvas {
    touch-action: none; /* 防止浏览器默认触摸行为 */
  }
}

/* 横屏模式 */
@media (orientation: landscape) and (max-height: 500px) {
  #three-container {
    height: 100vh;
  }
}

/* 暗色模式适配 */
@media (prefers-color-scheme: dark) {
  #three-container {
    background: #1a1a1a;
  }
}

/* 减少动画(用户偏好) */
@media (prefers-reduced-motion: reduce) {
  #three-container {
    animation-duration: 0.01ms !important;
  }
}

7. 7. 实用工具函数

javascript

class ThreeResponsiveUtils {
  // 获取安全区域(考虑刘海屏)
  static getSafeArea() {
    const style = getComputedStyle(document.documentElement);
    return {
      top: parseFloat(style.getPropertyValue('--safe-area-top')) || 0,
      right: parseFloat(style.getPropertyValue('--safe-area-right')) || 0,
      bottom: parseFloat(style.getPropertyValue('--safe-area-bottom')) || 0,
      left: parseFloat(style.getPropertyValue('--safe-area-left')) || 0
    };
  }

  // 检测屏幕方向
  static getOrientation() {
    if (window.matchMedia("(orientation: portrait)").matches) {
      return 'portrait';
    }
    return 'landscape';
  }

  // 设备类型检测
  static getDeviceType() {
    const width = window.innerWidth;
    if (width < 768) return 'mobile';
    if (width < 1024) return 'tablet';
    if (width < 1200) return 'desktop';
    return 'large-desktop';
  }

  // 防抖执行函数
  static debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }
}

8. 关键要点总结

  1. 及时更新:resize 事件中必须更新 camera.aspect 和调用 updateProjectionMatrix()

  2. 像素比优化:使用 Math.min(window.devicePixelRatio, 2) 避免过高的性能消耗

  3. 性能适配:根据设备能力动态调整渲染质量

  4. 内容响应:不同屏幕尺寸下调整物体位置和大小

  5. 内存管理:移除不需要的事件监听器,防止内存泄漏

  6. 移动端优化:考虑触摸交互和性能限制

这种响应式设计确保 Three.js 应用在各种设备上都能提供良好的用户体验。