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);
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);
}
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();
}
}
通过配置桌面端和移动端的数据,让场景特定内容根据配置信息能够在不同的设备上更好的展示。
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
);
}
}
});
}
}
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;
}
}
}
}
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();
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;
}
}
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);
};
}
}
及时更新:resize 事件中必须更新 camera.aspect 和调用 updateProjectionMatrix()
像素比优化:使用 Math.min(window.devicePixelRatio, 2) 避免过高的性能消耗
性能适配:根据设备能力动态调整渲染质量
内容响应:不同屏幕尺寸下调整物体位置和大小
内存管理:移除不需要的事件监听器,防止内存泄漏
移动端优化:考虑触摸交互和性能限制
这种响应式设计确保 Three.js 应用在各种设备上都能提供良好的用户体验。