前言
大家好,我是鲫小鱼。是一名不写前端代码的前端工程师,热衷于分享非前端的知识,带领切图仔逃离切图圈子,欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!
跨平台适配
在 Three.js 中,我们需要确保应用在不同设备和平台上都能正常运行。本章将介绍如何进行跨平台适配。
设备检测
1. 设备能力检测
图 20.1: 跨平台适配示例
// 设备能力检测器
class DeviceCapabilityDetector {
constructor() {
this.capabilities = {
webgl: this.checkWebGL(),
webgl2: this.checkWebGL2(),
touch: this.checkTouch(),
mobile: this.checkMobile(),
highDPI: this.checkHighDPI()
};
}
checkWebGL() {
try {
const canvas = document.createElement('canvas');
return !!(window.WebGLRenderingContext &&
(canvas.getContext('webgl') || canvas.getContext('experimental-webgl')));
} catch (e) {
return false;
}
}
checkWebGL2() {
try {
const canvas = document.createElement('canvas');
return !!(window.WebGL2RenderingContext && canvas.getContext('webgl2'));
} catch (e) {
return false;
}
}
checkTouch() {
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
}
checkMobile() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
checkHighDPI() {
return window.devicePixelRatio > 1;
}
getCapabilities() {
return this.capabilities;
}
}
2. 性能检测
// 性能检测器
class PerformanceDetector {
constructor() {
this.metrics = {
fps: 0,
frameTime: 0,
memory: 0
};
this.frameCount = 0;
this.lastTime = performance.now();
}
start() {
this.update();
}
update() {
const currentTime = performance.now();
const deltaTime = currentTime - this.lastTime;
this.frameCount++;
if (deltaTime >= 1000) {
this.metrics.fps = Math.round((this.frameCount * 1000) / deltaTime);
this.metrics.frameTime = deltaTime / this.frameCount;
if (performance.memory) {
this.metrics.memory = performance.memory.usedJSHeapSize / 1048576;
}
this.frameCount = 0;
this.lastTime = currentTime;
}
requestAnimationFrame(() => this.update());
}
getMetrics() {
return this.metrics;
}
}
响应式设计
1. 屏幕适配
// 屏幕适配器
class ScreenAdapter {
constructor(renderer, camera) {
this.renderer = renderer;
this.camera = camera;
this.aspectRatio = window.innerWidth / window.innerHeight;
this.updateSize();
window.addEventListener('resize', () => this.updateSize());
}
updateSize() {
const width = window.innerWidth;
const height = window.innerHeight;
// 更新渲染器
this.renderer.setSize(width, height);
// 更新相机
if (this.camera.isPerspectiveCamera) {
this.camera.aspect = width / height;
this.camera.updateProjectionMatrix();
}
}
getDevicePixelRatio() {
return Math.min(window.devicePixelRatio, 2);
}
setPixelRatio() {
this.renderer.setPixelRatio(this.getDevicePixelRatio());
}
}
2. 触摸适配
// 触摸适配器
class TouchAdapter {
constructor(camera, domElement) {
this.camera = camera;
this.domElement = domElement;
this.enabled = false;
this.target = new THREE.Vector3();
this.initialTouch = new THREE.Vector2();
this.currentTouch = new THREE.Vector2();
this.setupEventListeners();
}
setupEventListeners() {
this.domElement.addEventListener('touchstart', this.onTouchStart.bind(this));
this.domElement.addEventListener('touchmove', this.onTouchMove.bind(this));
this.domElement.addEventListener('touchend', this.onTouchEnd.bind(this));
}
onTouchStart(event) {
if (!this.enabled) return;
const touch = event.touches[0];
this.initialTouch.set(touch.clientX, touch.clientY);
this.currentTouch.copy(this.initialTouch);
}
onTouchMove(event) {
if (!this.enabled) return;
const touch = event.touches[0];
this.currentTouch.set(touch.clientX, touch.clientY);
const deltaX = this.currentTouch.x - this.initialTouch.x;
const deltaY = this.currentTouch.y - this.initialTouch.y;
this.rotateCamera(deltaX, deltaY);
}
onTouchEnd() {
if (!this.enabled) return;
// 处理触摸结束事件
}
rotateCamera(deltaX, deltaY) {
const rotationSpeed = 0.005;
this.camera.rotation.y -= deltaX * rotationSpeed;
this.camera.rotation.x -= deltaY * rotationSpeed;
// 限制垂直旋转角度
this.camera.rotation.x = Math.max(
-Math.PI / 2,
Math.min(Math.PI / 2, this.camera.rotation.x)
);
}
enable() {
this.enabled = true;
}
disable() {
this.enabled = false;
}
}
性能优化
1. 资源管理
// 资源管理器
class ResourceManager {
constructor() {
this.textures = new Map();
this.geometries = new Map();
this.materials = new Map();
this.loadingManager = new THREE.LoadingManager();
}
loadTexture(url) {
if (this.textures.has(url)) {
return this.textures.get(url);
}
const texture = new THREE.TextureLoader(this.loadingManager).load(url);
this.textures.set(url, texture);
return texture;
}
loadGeometry(url) {
if (this.geometries.has(url)) {
return this.geometries.get(url);
}
const geometry = new THREE.BufferGeometry();
// 加载几何体
this.geometries.set(url, geometry);
return geometry;
}
createMaterial(options) {
const key = JSON.stringify(options);
if (this.materials.has(key)) {
return this.materials.get(key);
}
const material = new THREE.MeshStandardMaterial(options);
this.materials.set(key, material);
return material;
}
dispose() {
this.textures.forEach(texture => texture.dispose());
this.geometries.forEach(geometry => geometry.dispose());
this.materials.forEach(material => material.dispose());
this.textures.clear();
this.geometries.clear();
this.materials.clear();
}
}
2. 性能监控
// 性能监控器
class PerformanceMonitor {
constructor() {
this.stats = new Stats();
this.stats.showPanel(0);
document.body.appendChild(this.stats.dom);
this.metrics = {
fps: 0,
frameTime: 0,
drawCalls: 0,
triangles: 0,
geometries: 0,
textures: 0
};
}
begin() {
this.stats.begin();
}
end() {
this.stats.end();
}
updateMetrics(renderer) {
const info = renderer.info;
this.metrics.drawCalls = info.render.calls;
this.metrics.triangles = info.render.triangles;
this.metrics.geometries = info.memory.geometries;
this.metrics.textures = info.memory.textures;
}
getMetrics() {
return this.metrics;
}
}
实战:跨平台应用
让我们创建一个完整的跨平台应用示例:
// 创建场景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// 设备检测
const deviceDetector = new DeviceCapabilityDetector();
const capabilities = deviceDetector.getCapabilities();
// 性能检测
const performanceDetector = new PerformanceDetector();
performanceDetector.start();
// 屏幕适配
const screenAdapter = new ScreenAdapter(renderer, camera);
screenAdapter.setPixelRatio();
// 触摸适配
const touchAdapter = new TouchAdapter(camera, renderer.domElement);
if (capabilities.touch) {
touchAdapter.enable();
}
// 资源管理
const resourceManager = new ResourceManager();
// 性能监控
const performanceMonitor = new PerformanceMonitor();
// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
directionalLight.castShadow = true;
scene.add(directionalLight);
// 创建物体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = resourceManager.createMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
scene.add(cube);
// 创建地面
const groundGeometry = new THREE.PlaneGeometry(10, 10);
const groundMaterial = resourceManager.createMaterial({ color: 0x808080 });
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
// 动画循环
function animate() {
requestAnimationFrame(animate);
// 性能监控开始
performanceMonitor.begin();
// 更新物体
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// 渲染场景
renderer.render(scene, camera);
// 更新性能指标
performanceMonitor.updateMetrics(renderer);
// 性能监控结束
performanceMonitor.end();
}
animate();
// 窗口大小改变时更新
window.addEventListener('resize', () => {
screenAdapter.updateSize();
});
// 页面卸载时清理资源
window.addEventListener('unload', () => {
resourceManager.dispose();
});
练习
- 实现设备检测
- 添加响应式设计
- 优化资源管理
- 实现性能监控
下一步学习
在下一章中,我们将学习:
- 性能优化
- 最佳实践
- 项目部署
- 发布上线
最后感谢阅读!欢迎关注我,微信公众号:
《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!!