第37节:移动端优化与触控交互
概述
移动端3D应用开发面临着性能限制、触控交互、电池续航等多重挑战。本节将深入探索移动端优化的核心技术,包括性能分析工具、渲染优化策略、触控交互设计,以及跨平台适配方案,打造流畅的移动端3D体验。

移动端优化系统架构:
flowchard TD
A[移动端优化系统] --> B[性能监控]
A --> C[渲染优化]
A --> D[触控交互]
A --> E[资源管理]
B --> B1[帧率监控]
B --> B2[内存分析]
B --> B3[功耗控制]
C --> C1[LOD系统]
C --> C2[分辨率缩放]
C --> C3[批次合并]
D --> D1[手势识别]
D --> D2[触觉反馈]
D --> D3[自适应UI]
E --> E1[资源压缩]
E --> E2[流式加载]
E --> E3[缓存策略]
B1 --> F[性能稳定]
C1 --> G[流畅渲染]
D1 --> H[自然交互]
E1 --> I[快速加载]
核心原理深度解析
移动端性能瓶颈分析
移动设备与桌面设备的性能差异:
| 性能指标 | 高端手机 | 中端手机 | 低端手机 | 桌面电脑 |
|---|---|---|---|---|
| GPU性能 | 1.5 TFLOPS | 0.5 TFLOPS | 0.2 TFLOPS | 10+ TFLOPS |
| 内存带宽 | 50 GB/s | 25 GB/s | 12 GB/s | 500+ GB/s |
| 电池容量 | 4000 mAh | 3000 mAh | 2000 mAh | 无限制 |
| 热限制 | 严格 | 中等 | 宽松 | 宽松 |
移动端渲染优化技术对比
| 优化技术 | 原理 | 性能提升 | 质量影响 | 实现复杂度 |
|---|---|---|---|---|
| 动态分辨率 | 根据帧率调整分辨率 | 30-50% | 轻微 | 简单 |
| LOD系统 | 距离相关细节层次 | 20-40% | 可控 | 中等 |
| 批次合并 | 减少绘制调用 | 40-60% | 无 | 复杂 |
| 纹理压缩 | 使用压缩纹理格式 | 20-30% | 轻微 | 简单 |
| 遮挡剔除 | 跳过不可见物体 | 10-50% | 无 | 复杂 |
完整代码实现
移动端优化3D应用
vue
<template>
<div class="mobile-optimization-container">
<!-- 3D场景画布 -->
<canvas ref="mobileCanvas" class="mobile-canvas"></canvas>
<!-- 移动端控制面板 -->
<div class="mobile-controls" :class="{ 'controls-collapsed': controlsCollapsed }">
<div class="controls-header" @click="toggleControls">
<h3>📱 移动端优化</h3>
<span class="collapse-icon">{{ controlsCollapsed ? '▶' : '▼' }}</span>
</div>
<div v-if="!controlsCollapsed" class="controls-content">
<div class="control-section">
<h4>🎯 性能设置</h4>
<div class="performance-presets">
<button
v-for="preset in performancePresets"
:key="preset.id"
@click="applyPerformancePreset(preset)"
class="preset-button"
:class="{ active: currentPreset?.id === preset.id }"
>
{{ preset.name }}
</button>
</div>
<div class="control-group">
<label>目标帧率: {{ targetFPS }} FPS</label>
<input
type="range"
v-model="targetFPS"
min="30"
max="120"
step="10"
>
</div>
<div class="control-group">
<label>分辨率缩放: {{ resolutionScale }}%</label>
<input
type="range"
v-model="resolutionScale"
min="25"
max="100"
step="5"
>
</div>
</div>
<div class="control-section">
<h4>🖐️ 触控设置</h4>
<div class="touch-controls">
<div class="control-group">
<label>触控灵敏度: {{ touchSensitivity }}</label>
<input
type="range"
v-model="touchSensitivity"
min="0.5"
max="2"
step="0.1"
>
</div>
<div class="control-group">
<label>惯性滚动: {{ inertiaStrength }}</label>
<input
type="range"
v-model="inertiaStrength"
min="0"
max="1"
step="0.1"
>
</div>
</div>
<div class="gesture-controls">
<div class="control-group">
<label>启用捏合缩放</label>
<input type="checkbox" v-model="pinchZoomEnabled">
</div>
<div class="control-group">
<label>启用旋转</label>
<input type="checkbox" v-model="rotationEnabled">
</div>
<div class="control-group">
<label>启用双击重置</label>
<input type="checkbox" v-model="doubleTapResetEnabled">
</div>
</div>
</div>
<div class="control-section">
<h4>⚡ 渲染优化</h4>
<div class="rendering-optimizations">
<div class="control-group">
<label>LOD级别: {{ lodLevel }}</label>
<input
type="range"
v-model="lodLevel"
min="1"
max="4"
step="1"
>
</div>
<div class="control-group">
<label>阴影质量: {{ shadowQuality }}</label>
<input
type="range"
v-model="shadowQuality"
min="0"
max="3"
step="1"
>
</div>
<div class="control-group">
<label>抗锯齿: {{ antiAliasing }}</label>
<input
type="range"
v-model="antiAliasing"
min="0"
max="2"
step="1"
>
</div>
</div>
<div class="optimization-toggles">
<div class="control-group">
<label>启用批次合并</label>
<input type="checkbox" v-model="batchingEnabled">
</div>
<div class="control-group">
<label>启用视锥剔除</label>
<input type="checkbox" v-model="frustumCullingEnabled">
</div>
<div class="control-group">
<label>启用纹理压缩</label>
<input type="checkbox" v-model="textureCompressionEnabled">
</div>
</div>
</div>
<div class="control-section">
<h4>🔋 电池优化</h4>
<div class="battery-optimizations">
<div class="control-group">
<label>功耗模式: {{ powerMode }}</label>
<select v-model="powerMode" class="mode-select">
<option value="performance">性能优先</option>
<option value="balanced">平衡模式</option>
<option value="battery">省电模式</option>
</select>
</div>
<div class="control-group">
<label>后台暂停: {{ backgroundPauseDelay }}s</label>
<input
type="range"
v-model="backgroundPauseDelay"
min="1"
max="10"
step="1"
>
</div>
</div>
<div class="thermal-controls">
<div class="control-group">
<label>温度限制: {{ thermalLimit }}°C</label>
<input
type="range"
v-model="thermalLimit"
min="40"
max="60"
step="5"
>
</div>
</div>
</div>
<div class="control-section">
<h4>📊 性能监控</h4>
<div class="performance-stats">
<div class="stat-item">
<span>当前帧率:</span>
<span :class="getFPSClass(currentFPS)">{{ currentFPS }} FPS</span>
</div>
<div class="stat-item">
<span>帧时间:</span>
<span>{{ frameTime.toFixed(1) }}ms</span>
</div>
<div class="stat-item">
<span>内存使用:</span>
<span>{{ formatMemory(memoryUsage) }}</span>
</div>
<div class="stat-item">
<span>绘制调用:</span>
<span>{{ drawCalls }}</span>
</div>
<div class="stat-item">
<span>三角形数量:</span>
<span>{{ formatNumber(triangleCount) }}</span>
</div>
<div class="stat-item">
<span>电池状态:</span>
<span :class="getBatteryClass(batteryLevel)">{{ batteryLevel }}%</span>
</div>
</div>
</div>
</div>
</div>
<!-- 触控控制区域 -->
<div class="touch-control-area">
<div class="virtual-joystick"
@touchstart="onJoystickStart"
@touchmove="onJoystickMove"
@touchend="onJoystickEnd">
<div class="joystick-base">
<div class="joystick-handle" :style="joystickStyle"></div>
</div>
</div>
<div class="action-buttons">
<button class="action-button jump" @touchstart="onJumpTouch">跳跃</button>
<button class="action-button interact" @touchstart="onInteractTouch">交互</button>
</div>
</div>
<!-- 手势提示 -->
<div class="gesture-hints" v-if="showGestureHints">
<div class="hint-item">
<span class="hint-icon">🤏</span>
<span>捏合缩放</span>
</div>
<div class="hint-item">
<span class="hint-icon">👆</span>
<span>拖拽旋转</span>
</div>
<div class="hint-item">
<span class="hint-icon">👆👆</span>
<span>双击重置</span>
</div>
</div>
<!-- 性能警告 -->
<div v-if="showPerformanceWarning" class="performance-warning">
<div class="warning-content">
<span class="warning-icon">⚠️</span>
<span>性能警告: 帧率过低 ({{ currentFPS }} FPS)</span>
<button @click="applyPerformancePreset(performancePresets[2])" class="optimize-button">
自动优化
</button>
</div>
</div>
<!-- 电池警告 -->
<div v-if="showBatteryWarning" class="battery-warning">
<div class="warning-content">
<span class="warning-icon">🔋</span>
<span>电池电量低 ({{ batteryLevel }}%)</span>
<button @click="enablePowerSaving" class="power-save-button">
开启省电模式
</button>
</div>
</div>
<!-- 加载界面 -->
<div v-if="isLoading" class="loading-overlay">
<div class="loading-content">
<div class="mobile-loader">
<div class="phone-frame">
<div class="screen-content">
<div class="loading-bar"></div>
<div class="loading-dots">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
</div>
</div>
<h3>优化移动端体验...</h3>
<div class="loading-progress">
<div class="progress-bar">
<div class="progress-fill" :style="loadingProgressStyle"></div>
</div>
<span>{{ loadingMessage }}</span>
</div>
<div class="optimization-stats">
<div class="stat">压缩纹理: {{ compressedTextures }}/{{ totalTextures }}</div>
<div class="stat">优化网格: {{ optimizedMeshes }}/{{ totalMeshes }}</div>
<div class="stat">加载资源: {{ formatMemory(loadedMemory) }}/{{ formatMemory(totalMemory) }}</div>
</div>
</div>
</div>
<!-- 调试信息 -->
<div class="debug-overlay" v-if="showDebugOverlay">
<div class="debug-info">
<div>设备: {{ deviceInfo }}</div>
<div>GPU: {{ gpuInfo }}</div>
<div>平台: {{ platformInfo }}</div>
<div>触控点: {{ touchPoints }} 个</div>
<div>温度: {{ deviceTemperature }}°C</div>
</div>
</div>
<!-- 快捷操作栏 -->
<div class="quick-actions">
<button @click="toggleDebug" class="quick-button" :class="{ active: showDebugOverlay }">
🐛
</button>
<button @click="toggleStats" class="quick-button" :class="{ active: showStats }">
📊
</button>
<button @click="screenshot" class="quick-button">
📸
</button>
<button @click="toggleFullscreen" class="quick-button">
{{ isFullscreen ? '📱' : '🔲' }}
</button>
</div>
</div>
</template>
<script>
import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
import * as THREE from 'three';
// 移动端性能监控器
class MobilePerformanceMonitor {
constructor() {
this.fpsHistory = [];
this.frameTimeHistory = [];
this.memoryHistory = [];
this.maxHistorySize = 60; // 保存60帧数据
this.lowFPSThreshold = 30;
this.highFrameTimeThreshold = 33; // 对应30FPS
this.memoryWarningThreshold = 100 * 1024 * 1024; // 100MB
this.startTime = performance.now();
this.frameCount = 0;
}
// 开始监控
start() {
this.startTime = performance.now();
this.frameCount = 0;
}
// 更新监控数据
update() {
const currentTime = performance.now();
const frameTime = currentTime - this.lastFrameTime || 0;
this.lastFrameTime = currentTime;
// 计算FPS
this.frameCount++;
if (currentTime - this.startTime >= 1000) {
const fps = Math.round((this.frameCount * 1000) / (currentTime - this.startTime));
this.fpsHistory.push(fps);
if (this.fpsHistory.length > this.maxHistorySize) {
this.fpsHistory.shift();
}
this.frameCount = 0;
this.startTime = currentTime;
}
// 记录帧时间
this.frameTimeHistory.push(frameTime);
if (this.frameTimeHistory.length > this.maxHistorySize) {
this.frameTimeHistory.shift();
}
return {
fps: this.fpsHistory[this.fpsHistory.length - 1] || 0,
frameTime: frameTime,
averageFPS: this.getAverageFPS(),
averageFrameTime: this.getAverageFrameTime()
};
}
// 获取平均FPS
getAverageFPS() {
if (this.fpsHistory.length === 0) return 0;
return Math.round(this.fpsHistory.reduce((a, b) => a + b) / this.fpsHistory.length);
}
// 获取平均帧时间
getAverageFrameTime() {
if (this.frameTimeHistory.length === 0) return 0;
return this.frameTimeHistory.reduce((a, b) => a + b) / this.frameTimeHistory.length;
}
// 检查性能问题
checkPerformanceIssues() {
const currentFPS = this.fpsHistory[this.fpsHistory.length - 1] || 0;
const averageFrameTime = this.getAverageFrameTime();
const issues = [];
if (currentFPS < this.lowFPSThreshold) {
issues.push({
type: 'low_fps',
severity: currentFPS < 20 ? 'high' : 'medium',
message: `帧率过低: ${currentFPS} FPS`
});
}
if (averageFrameTime > this.highFrameTimeThreshold) {
issues.push({
type: 'high_frame_time',
severity: 'medium',
message: `帧时间过长: ${averageFrameTime.toFixed(1)}ms`
});
}
return issues;
}
// 获取性能建议
getPerformanceSuggestions(issues) {
const suggestions = [];
if (issues.some(issue => issue.type === 'low_fps')) {
suggestions.push('降低分辨率缩放');
suggestions.push('减少阴影质量');
suggestions.push('启用批次合并');
}
if (issues.some(issue => issue.type === 'high_frame_time')) {
suggestions.push('简化场景复杂度');
suggestions.push('启用LOD系统');
suggestions.push('减少绘制调用');
}
return suggestions;
}
}
// 移动端触控控制器
class MobileTouchController {
constructor(domElement, camera, renderer) {
this.domElement = domElement;
this.camera = camera;
this.renderer = renderer;
this.touchState = {
isRotating: false,
isZooming: false,
isPanning: false,
lastTouchX: 0,
lastTouchY: 0,
scale: 1,
touches: []
};
this.sensitivity = 1.0;
this.inertia = 0.9;
this.velocity = new THREE.Vector2();
this.setupEventListeners();
}
// 设置事件监听器
setupEventListeners() {
this.domElement.addEventListener('touchstart', this.onTouchStart.bind(this), { passive: false });
this.domElement.addEventListener('touchmove', this.onTouchMove.bind(this), { passive: false });
this.domElement.addEventListener('touchend', this.onTouchEnd.bind(this), { passive: false });
this.domElement.addEventListener('touchcancel', this.onTouchEnd.bind(this), { passive: false });
}
// 触摸开始
onTouchStart(event) {
event.preventDefault();
this.touchState.touches = Array.from(event.touches);
if (event.touches.length === 1) {
// 单指触摸 - 旋转
this.touchState.isRotating = true;
this.touchState.lastTouchX = event.touches[0].clientX;
this.touchState.lastTouchY = event.touches[0].clientY;
} else if (event.touches.length === 2) {
// 双指触摸 - 缩放/平移
this.touchState.isZooming = true;
this.touchState.lastTouchDistance = this.getTouchDistance(event.touches);
}
// 重置速度
this.velocity.set(0, 0);
}
// 触摸移动
onTouchMove(event) {
event.preventDefault();
const touches = Array.from(event.touches);
this.touchState.touches = touches;
if (this.touchState.isRotating && touches.length === 1) {
this.handleRotation(touches[0]);
} else if (this.touchState.isZooming && touches.length === 2) {
this.handleZoom(touches);
}
// 更新最后触摸位置
if (touches.length === 1) {
this.touchState.lastTouchX = touches[0].clientX;
this.touchState.lastTouchY = touches[0].clientY;
}
}
// 触摸结束
onTouchEnd(event) {
event.preventDefault();
const touches = Array.from(event.touches);
this.touchState.touches = touches;
if (touches.length === 0) {
// 所有手指抬起
this.touchState.isRotating = false;
this.touchState.isZooming = false;
this.touchState.isPanning = false;
} else if (touches.length === 1) {
// 从双指变为单指
this.touchState.isZooming = false;
this.touchState.isRotating = true;
this.touchState.lastTouchX = touches[0].clientX;
this.touchState.lastTouchY = touches[0].clientY;
}
}
// 处理旋转
handleRotation(touch) {
const deltaX = (touch.clientX - this.touchState.lastTouchX) * this.sensitivity * 0.01;
const deltaY = (touch.clientY - this.touchState.lastTouchY) * this.sensitivity * 0.01;
// 更新相机旋转
if (this.camera.rotation) {
this.camera.rotation.y -= deltaX;
this.camera.rotation.x -= deltaY;
this.camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.camera.rotation.x));
}
// 更新速度
this.velocity.x = deltaX;
this.velocity.y = deltaY;
}
// 处理缩放
handleZoom(touches) {
const currentDistance = this.getTouchDistance(touches);
const deltaDistance = currentDistance - this.touchState.lastTouchDistance;
// 更新缩放
const zoomFactor = 1 + deltaDistance * 0.01;
if (this.camera.zoom !== undefined) {
this.camera.zoom = Math.max(0.1, Math.min(5, this.camera.zoom * zoomFactor));
this.camera.updateProjectionMatrix();
}
this.touchState.lastTouchDistance = currentDistance;
}
// 获取触摸点距离
getTouchDistance(touches) {
const dx = touches[0].clientX - touches[1].clientX;
const dy = touches[0].clientY - touches[1].clientY;
return Math.sqrt(dx * dx + dy * dy);
}
// 更新惯性
update(deltaTime) {
if (!this.touchState.isRotating && (Math.abs(this.velocity.x) > 0.001 || Math.abs(this.velocity.y) > 0.001)) {
// 应用惯性
if (this.camera.rotation) {
this.camera.rotation.y -= this.velocity.x * deltaTime * 10;
this.camera.rotation.x -= this.velocity.y * deltaTime * 10;
this.camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.camera.rotation.x));
}
// 衰减速度
this.velocity.multiplyScalar(this.inertia);
}
}
// 设置灵敏度
setSensitivity(sensitivity) {
this.sensitivity = sensitivity;
}
// 设置惯性
setInertia(inertia) {
this.inertia = inertia;
}
}
// 移动端渲染优化器
class MobileRenderOptimizer {
constructor(renderer, scene, camera) {
this.renderer = renderer;
this.scene = scene;
this.camera = camera;
this.optimizationState = {
resolutionScale: 1.0,
lodEnabled: true,
shadowsEnabled: true,
shadowsQuality: 1,
batchingEnabled: false,
textureCompressionEnabled: true
};
this.setupOptimizations();
}
// 设置优化
setupOptimizations() {
// 设置移动端友好的渲染参数
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
this.renderer.physicallyCorrectLights = false;
this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
this.renderer.toneMappingExposure = 1.0;
// 启用基本的优化
this.enableBasicOptimizations();
}
// 启用基础优化
enableBasicOptimizations() {
// 使用更高效的阴影映射
if (this.optimizationState.shadowsEnabled) {
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
}
// 设置适当的抗锯齿
this.renderer.antialias = this.optimizationState.resolutionScale >= 0.7;
}
// 应用分辨率缩放
applyResolutionScale(scale) {
this.optimizationState.resolutionScale = scale;
const width = Math.floor(window.innerWidth * scale);
const height = Math.floor(window.innerHeight * scale);
this.renderer.setSize(width, height, false);
if (this.camera.isPerspectiveCamera) {
this.camera.aspect = width / height;
this.camera.updateProjectionMatrix();
}
}
// 应用LOD优化
applyLODOptimization(level) {
this.scene.traverse((object) => {
if (object.isMesh && object.geometry) {
// 根据LOD级别简化几何体
const detailLevel = Math.max(1, Math.floor(level));
this.optimizeGeometry(object.geometry, detailLevel);
}
});
}
// 优化几何体
optimizeGeometry(geometry, level) {
// 简化实现 - 实际应该使用更复杂的LOD系统
if (level < 3 && geometry.attributes.position && geometry.attributes.position.count > 500) {
// 对于复杂网格,可以在这里应用简化
// 实际实现应该使用 THREE.SimplifyModifier 或类似工具
}
}
// 应用阴影质量设置
applyShadowQuality(quality) {
this.optimizationState.shadowsQuality = quality;
this.scene.traverse((object) => {
if (object.isLight && object.shadow) {
const resolution = this.getShadowResolution(quality);
object.shadow.mapSize.set(resolution, resolution);
object.shadow.needsUpdate = true;
}
});
}
// 获取阴影分辨率
getShadowResolution(quality) {
switch (quality) {
case 0: return 512; // 低质量
case 1: return 1024; // 中等质量
case 2: return 2048; // 高质量
case 3: return 4096; // 超高质量
default: return 1024;
}
}
// 应用批次合并
applyBatchingOptimization() {
if (!this.optimizationState.batchingEnabled) return;
// 简化实现 - 实际应该使用 THREE.BufferGeometryUtils.mergeBufferGeometries
console.log('应用批次合并优化...');
}
// 应用纹理压缩
applyTextureCompression() {
if (!this.optimizationState.textureCompressionEnabled) return;
this.scene.traverse((object) => {
if (object.isMesh && object.material) {
this.compressTexture(object.material);
}
});
}
// 压缩纹理
compressTexture(material) {
// 简化实现 - 实际应该使用压缩纹理格式
if (material.map) {
material.map.minFilter = THREE.LinearMipmapLinearFilter;
material.map.generateMipmaps = true;
}
}
// 获取性能统计
getPerformanceStats() {
const info = this.renderer.info;
return {
memory: {
geometries: info.memory.geometries,
textures: info.memory.textures
},
render: {
calls: info.render.calls,
triangles: info.render.triangles,
points: info.render.points,
lines: info.render.lines
}
};
}
// 应用性能预设
applyPerformancePreset(preset) {
switch (preset) {
case 'low':
this.optimizationState.resolutionScale = 0.5;
this.optimizationState.shadowsEnabled = false;
this.optimizationState.shadowsQuality = 0;
this.optimizationState.lodEnabled = true;
break;
case 'medium':
this.optimizationState.resolutionScale = 0.75;
this.optimizationState.shadowsEnabled = true;
this.optimizationState.shadowsQuality = 1;
this.optimizationState.lodEnabled = true;
break;
case 'high':
this.optimizationState.resolutionScale = 1.0;
this.optimizationState.shadowsEnabled = true;
this.optimizationState.shadowsQuality = 2;
this.optimizationState.lodEnabled = false;
break;
}
this.applyAllOptimizations();
}
// 应用所有优化
applyAllOptimizations() {
this.applyResolutionScale(this.optimizationState.resolutionScale);
this.applyShadowQuality(this.optimizationState.shadowsQuality);
this.applyBatchingOptimization();
this.applyTextureCompression();
if (this.optimizationState.lodEnabled) {
this.applyLODOptimization(2); // 中等LOD级别
}
}
}
// 移动端电池管理器
class MobileBatteryManager {
constructor() {
this.batteryLevel = 1.0;
this.isCharging = false;
this.chargeTime = 0;
this.dischargeTime = 0;
this.powerSavingMode = false;
this.thermalThrottling = false;
this.setupBatteryMonitoring();
}
// 设置电池监控
async setupBatteryMonitoring() {
if ('getBattery' in navigator) {
try {
const battery = await navigator.getBattery();
this.updateBatteryInfo(battery);
battery.addEventListener('levelchange', () => this.updateBatteryInfo(battery));
battery.addEventListener('chargingchange', () => this.updateBatteryInfo(battery));
battery.addEventListener('chargingtimechange', () => this.updateBatteryInfo(battery));
battery.addEventListener('dischargingtimechange', () => this.updateBatteryInfo(battery));
} catch (error) {
console.warn('电池API不支持:', error);
this.simulateBattery();
}
} else {
console.warn('电池API不可用');
this.simulateBattery();
}
}
// 更新电池信息
updateBatteryInfo(battery) {
this.batteryLevel = battery.level;
this.isCharging = battery.charging;
this.chargeTime = battery.chargingTime;
this.dischargeTime = battery.dischargingTime;
}
// 模拟电池(用于不支持API的设备)
simulateBattery() {
// 模拟电池消耗
setInterval(() => {
if (!this.isCharging) {
this.batteryLevel = Math.max(0, this.batteryLevel - 0.001);
} else {
this.batteryLevel = Math.min(1, this.batteryLevel + 0.005);
}
}, 1000);
}
// 启用省电模式
enablePowerSaving() {
this.powerSavingMode = true;
// 应用省电优化
this.applyPowerSavingOptimizations();
return {
resolutionScale: 0.5,
frameRateLimit: 30,
shadowsEnabled: false,
reflectionsEnabled: false,
particleEffects: false
};
}
// 禁用省电模式
disablePowerSaving() {
this.powerSavingMode = false;
}
// 应用省电优化
applyPowerSavingOptimizations() {
// 这些优化应该应用到渲染系统中
console.log('应用省电优化...');
}
// 检查电池状态
checkBatteryStatus() {
const status = {
level: this.batteryLevel,
isCharging: this.isCharging,
isLow: this.batteryLevel < 0.2,
isCritical: this.batteryLevel < 0.1
};
return status;
}
// 获取电池建议
getBatterySuggestions() {
const suggestions = [];
const status = this.checkBatteryStatus();
if (status.isLow && !this.powerSavingMode) {
suggestions.push('启用省电模式');
}
if (status.isCritical) {
suggestions.push('建议连接充电器');
}
return suggestions;
}
}
export default {
name: 'MobileOptimization',
setup() {
const mobileCanvas = ref(null);
const controlsCollapsed = ref(false);
const targetFPS = ref(60);
const resolutionScale = ref(75);
const touchSensitivity = ref(1.0);
const inertiaStrength = ref(0.8);
const pinchZoomEnabled = ref(true);
const rotationEnabled = ref(true);
const doubleTapResetEnabled = ref(true);
const lodLevel = ref(2);
const shadowQuality = ref(1);
const antiAliasing = ref(1);
const batchingEnabled = ref(true);
const frustumCullingEnabled = ref(true);
const textureCompressionEnabled = ref(true);
const powerMode = ref('balanced');
const backgroundPauseDelay = ref(3);
const thermalLimit = ref(45);
const currentFPS = ref(0);
const frameTime = ref(0);
const memoryUsage = ref(0);
const drawCalls = ref(0);
const triangleCount = ref(0);
const batteryLevel = ref(100);
const showGestureHints = ref(true);
const showPerformanceWarning = ref(false);
const showBatteryWarning = ref(false);
const isLoading = ref(true);
const loadingMessage = ref('初始化移动端优化...');
const compressedTextures = ref(0);
const totalTextures = ref(50);
const optimizedMeshes = ref(0);
const totalMeshes = ref(20);
const loadedMemory = ref(0);
const totalMemory = ref(50 * 1024 * 1024);
const showDebugOverlay = ref(false);
const showStats = ref(true);
const isFullscreen = ref(false);
const joystickPosition = reactive({ x: 0, y: 0 });
const performancePresets = [
{ id: 'low', name: '低功耗' },
{ id: 'medium', name: '平衡' },
{ id: 'high', name: '高性能' },
{ id: 'auto', name: '自动' }
];
let currentPreset = ref(performancePresets[1]);
let scene, camera, renderer, touchController, performanceMonitor, renderOptimizer, batteryManager;
let clock, stats;
let frameCount = 0;
let lastFpsUpdate = 0;
// 设备信息
const deviceInfo = computed(() => {
return isMobileDevice() ? '移动设备' : '桌面设备';
});
const gpuInfo = computed(() => {
return renderer ? renderer.capabilities.getMaxAnisotropy() + 'x 各向异性' : '未知';
});
const platformInfo = computed(() => {
const ua = navigator.userAgent;
if (/android/i.test(ua)) return 'Android';
if (/iPad|iPhone|iPod/.test(ua)) return 'iOS';
if (/Windows/.test(ua)) return 'Windows';
if (/Mac/.test(ua)) return 'macOS';
if (/Linux/.test(ua)) return 'Linux';
return '未知';
});
const touchPoints = ref(0);
const deviceTemperature = ref(35);
// 初始化场景
const initScene = async () => {
// 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb);
// 创建相机
camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 5, 10);
// 创建渲染器
renderer = new THREE.WebGLRenderer({
canvas: mobileCanvas.value,
antialias: true,
powerPreference: "high-performance"
});
// 初始化性能监控器
performanceMonitor = new MobilePerformanceMonitor();
performanceMonitor.start();
// 初始化渲染优化器
renderOptimizer = new MobileRenderOptimizer(renderer, scene, camera);
// 初始化触控控制器
touchController = new MobileTouchController(renderer.domElement, camera, renderer);
// 初始化电池管理器
batteryManager = new MobileBatteryManager();
// 设置初始优化
applyCurrentOptimizations();
// 创建测试场景
loadingMessage.value = '创建优化场景...';
await createOptimizedScene();
// 设置事件监听
setupEventListeners();
isLoading.value = false;
// 启动渲染循环
clock = new THREE.Clock();
animate();
};
// 创建优化场景
const createOptimizedScene = async () => {
// 添加光照
const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(50, 50, 25);
directionalLight.castShadow = true;
scene.add(directionalLight);
// 创建优化地面
const groundGeometry = new THREE.PlaneGeometry(100, 100);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x3a9d23,
roughness: 0.8,
metalness: 0.2
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
// 创建优化物体(使用简单的几何体)
createOptimizedObjects();
// 模拟加载过程
await simulateOptimizationProcess();
};
// 创建优化物体
const createOptimizedObjects = () => {
// 使用简单几何体创建场景物体
const geometries = [
new THREE.BoxGeometry(2, 2, 2),
new THREE.SphereGeometry(1, 16, 16),
new THREE.ConeGeometry(1, 3, 8),
new THREE.CylinderGeometry(1, 1, 2, 12)
];
const materials = [
new THREE.MeshStandardMaterial({ color: 0xff4444 }),
new THREE.MeshStandardMaterial({ color: 0x44ff44 }),
new THREE.MeshStandardMaterial({ color: 0x4444ff }),
new THREE.MeshStandardMaterial({ color: 0xffff44 })
];
for (let i = 0; i < 20; i++) {
const geometry = geometries[i % geometries.length];
const material = materials[i % materials.length];
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(
(Math.random() - 0.5) * 40,
Math.random() * 5,
(Math.random() - 0.5) * 40
);
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add(mesh);
}
};
// 模拟优化过程
const simulateOptimizationProcess = async () => {
const steps = [
'压缩纹理...',
'优化网格...',
'合并批次...',
'设置LOD...',
'应用压缩...'
];
for (let i = 0; i < steps.length; i++) {
loadingMessage.value = steps[i];
compressedTextures.value = Math.floor((i + 1) / steps.length * totalTextures.value);
optimizedMeshes.value = Math.floor((i + 1) / steps.length * totalMeshes.value);
loadedMemory.value = Math.floor((i + 1) / steps.length * totalMemory.value);
await new Promise(resolve => setTimeout(resolve, 500));
}
};
// 设置事件监听
const setupEventListeners = () => {
// 窗口大小变化
window.addEventListener('resize', onWindowResize);
// 电池状态变化
setInterval(updateBatteryStatus, 5000);
// 设备温度模拟
setInterval(updateDeviceTemperature, 10000);
// 隐藏手势提示
setTimeout(() => {
showGestureHints.value = false;
}, 5000);
};
// 窗口大小变化
const onWindowResize = () => {
if (!camera || !renderer) return;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderOptimizer.applyResolutionScale(resolutionScale.value / 100);
};
// 更新电池状态
const updateBatteryStatus = () => {
const status = batteryManager.checkBatteryStatus();
batteryLevel.value = Math.round(status.level * 100);
// 显示电池警告
showBatteryWarning.value = status.isLow && !batteryManager.powerSavingMode;
};
// 更新设备温度
const updateDeviceTemperature = () => {
// 模拟设备温度变化
const baseTemp = 35;
const loadFactor = currentFPS.value < 30 ? 0.3 : 0.1;
const randomVariation = (Math.random() - 0.5) * 2;
deviceTemperature.value = Math.max(
30,
Math.min(60, baseTemp + loadFactor * 10 + randomVariation)
);
// 温度警告
if (deviceTemperature.value > thermalLimit.value) {
applyPerformancePreset(performancePresets[0]); // 切换到低功耗模式
}
};
// 应用性能预设
const applyPerformancePreset = (preset) => {
currentPreset.value = preset;
switch (preset.id) {
case 'low':
resolutionScale.value = 50;
shadowQuality.value = 0;
targetFPS.value = 30;
powerMode.value = 'battery';
break;
case 'medium':
resolutionScale.value = 75;
shadowQuality.value = 1;
targetFPS.value = 60;
powerMode.value = 'balanced';
break;
case 'high':
resolutionScale.value = 100;
shadowQuality.value = 2;
targetFPS.value = 120;
powerMode.value = 'performance';
break;
case 'auto':
// 基于设备性能自动选择
applyAutoOptimization();
break;
}
applyCurrentOptimizations();
};
// 应用自动优化
const applyAutoOptimization = () => {
// 基于设备性能自动选择设置
const isLowEndDevice = isMobileDevice() && !isHighEndDevice();
if (isLowEndDevice) {
applyPerformancePreset(performancePresets[0]);
} else {
applyPerformancePreset(performancePresets[1]);
}
};
// 应用当前优化设置
const applyCurrentOptimizations = () => {
if (!renderOptimizer) return;
// 应用分辨率缩放
renderOptimizer.applyResolutionScale(resolutionScale.value / 100);
// 应用阴影质量
renderOptimizer.applyShadowQuality(shadowQuality.value);
// 应用LOD优化
renderOptimizer.applyLODOptimization(lodLevel.value);
// 应用触控设置
if (touchController) {
touchController.setSensitivity(touchSensitivity.value);
touchController.setInertia(inertiaStrength.value);
}
};
// 检查设备类型
const isMobileDevice = () => {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
};
// 检查是否高端设备
const isHighEndDevice = () => {
// 简化检测 - 实际应该使用更复杂的特征检测
const hasHighDpi = window.devicePixelRatio >= 2;
const hasLargeScreen = window.innerWidth >= 400;
return hasHighDpi && hasLargeScreen;
};
// 切换控制面板
const toggleControls = () => {
controlsCollapsed.value = !controlsCollapsed.value;
};
// 虚拟摇杆控制
const onJoystickStart = (event) => {
event.preventDefault();
// 实际实现应该处理触摸事件
};
const onJoystickMove = (event) => {
event.preventDefault();
// 实际实现应该处理触摸移动
};
const onJoystickEnd = (event) => {
event.preventDefault();
joystickPosition.x = 0;
joystickPosition.y = 0;
};
// 动作按钮
const onJumpTouch = () => {
console.log('跳跃动作');
// 实际实现应该触发跳跃动画
};
const onInteractTouch = () => {
console.log('交互动作');
// 实际实现应该触发交互逻辑
};
// 启用省电模式
const enablePowerSaving = () => {
batteryManager.enablePowerSaving();
applyPerformancePreset(performancePresets[0]);
showBatteryWarning.value = false;
};
// 切换调试信息
const toggleDebug = () => {
showDebugOverlay.value = !showDebugOverlay.value;
};
// 切换统计信息
const toggleStats = () => {
showStats.value = !showStats.value;
};
// 截图功能
const screenshot = () => {
if (!renderer) return;
renderer.domElement.toBlob((blob) => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `screenshot-${Date.now()}.png`;
a.click();
URL.revokeObjectURL(url);
});
};
// 切换全屏
const toggleFullscreen = () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(err => {
console.log(`全屏请求错误: ${err.message}`);
});
isFullscreen.value = true;
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
isFullscreen.value = false;
}
}
};
// 动画循环
const animate = () => {
requestAnimationFrame(animate);
const deltaTime = clock.getDelta();
// 更新性能监控
const performanceData = performanceMonitor.update();
currentFPS.value = performanceData.fps;
frameTime.value = performanceData.frameTime;
// 更新触控控制器
if (touchController) {
touchController.update(deltaTime);
}
// 更新性能统计
updatePerformanceStats();
// 检查性能问题
const issues = performanceMonitor.checkPerformanceIssues();
showPerformanceWarning.value = issues.length > 0;
// 应用动态优化
applyDynamicOptimizations(issues);
// 渲染场景
renderer.render(scene, camera);
// 更新FPS计数
updateFPSCounter(deltaTime);
};
// 更新性能统计
const updatePerformanceStats = () => {
if (!renderOptimizer) return;
const stats = renderOptimizer.getPerformanceStats();
memoryUsage.value = stats.memory.geometries * 1024 * 1024 + stats.memory.textures * 512 * 1024;
drawCalls.value = stats.render.calls;
triangleCount.value = stats.render.triangles;
};
// 应用动态优化
const applyDynamicOptimizations = (issues) => {
if (currentPreset.value.id === 'auto') {
// 自动模式下的动态优化
if (issues.some(issue => issue.type === 'low_fps')) {
// 如果帧率低,降低质量
resolutionScale.value = Math.max(25, resolutionScale.value - 5);
applyCurrentOptimizations();
} else if (currentFPS.value > targetFPS.value + 10 && resolutionScale.value < 100) {
// 如果性能充足,提高质量
resolutionScale.value = Math.min(100, resolutionScale.value + 5);
applyCurrentOptimizations();
}
}
};
// 更新FPS计数器
const updateFPSCounter = (deltaTime) => {
frameCount++;
lastFpsUpdate += deltaTime;
if (lastFpsUpdate >= 1.0) {
currentFPS.value = Math.round(frameCount / lastFpsUpdate);
frameCount = 0;
lastFpsUpdate = 0;
}
};
// 计算属性
const joystickStyle = computed(() => ({
transform: `translate(${joystickPosition.x * 20}px, ${joystickPosition.y * 20}px)`
}));
const loadingProgressStyle = computed(() => ({
width: '100%'
}));
// 工具函数
const getFPSClass = (fps) => {
if (fps >= 50) return 'good';
if (fps >= 30) return 'warning';
return 'bad';
};
const getBatteryClass = (level) => {
if (level >= 50) return 'good';
if (level >= 20) return 'warning';
return 'bad';
};
const formatMemory = (bytes) => {
if (bytes >= 1024 * 1024) {
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
} else if (bytes >= 1024) {
return (bytes / 1024).toFixed(1) + ' KB';
}
return bytes + ' B';
};
const formatNumber = (num) => {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + 'M';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num.toString();
};
onMounted(() => {
initScene();
});
onUnmounted(() => {
if (renderer) {
renderer.dispose();
}
window.removeEventListener('resize', onWindowResize);
});
return {
mobileCanvas,
controlsCollapsed,
targetFPS,
resolutionScale,
touchSensitivity,
inertiaStrength,
pinchZoomEnabled,
rotationEnabled,
doubleTapResetEnabled,
lodLevel,
shadowQuality,
antiAliasing,
batchingEnabled,
frustumCullingEnabled,
textureCompressionEnabled,
powerMode,
backgroundPauseDelay,
thermalLimit,
currentFPS,
frameTime,
memoryUsage,
drawCalls,
triangleCount,
batteryLevel,
showGestureHints,
showPerformanceWarning,
showBatteryWarning,
isLoading,
loadingMessage,
compressedTextures,
totalTextures,
optimizedMeshes,
totalMeshes,
loadedMemory,
totalMemory,
showDebugOverlay,
showStats,
isFullscreen,
joystickPosition,
performancePresets,
currentPreset,
deviceInfo,
gpuInfo,
platformInfo,
touchPoints,
deviceTemperature,
joystickStyle,
loadingProgressStyle,
toggleControls,
applyPerformancePreset,
onJoystickStart,
onJoystickMove,
onJoystickEnd,
onJumpTouch,
onInteractTouch,
enablePowerSaving,
toggleDebug,
toggleStats,
screenshot,
toggleFullscreen,
getFPSClass,
getBatteryClass,
formatMemory,
formatNumber
};
}
};
</script>
<style scoped>
.mobile-optimization-container {
width: 100%;
height: 100vh;
position: relative;
background: #000;
overflow: hidden;
touch-action: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
.mobile-canvas {
width: 100%;
height: 100%;
display: block;
}
.mobile-controls {
position: absolute;
top: 10px;
right: 10px;
width: 320px;
max-width: 90vw;
background: rgba(0, 0, 0, 0.95);
border-radius: 12px;
color: white;
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
overflow: hidden;
}
.mobile-controls.controls-collapsed {
height: 50px;
}
.controls-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
cursor: pointer;
background: rgba(0, 0, 0, 0.8);
}
.controls-header h3 {
margin: 0;
color: #00ff88;
font-size: 16px;
}
.collapse-icon {
color: #ccc;
font-size: 14px;
}
.controls-content {
padding: 15px;
max-height: 70vh;
overflow-y: auto;
}
.control-section {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.control-section:last-child {
margin-bottom: 0;
border-bottom: none;
}
.control-section h4 {
color: #00aaff;
margin-bottom: 15px;
font-size: 14px;
}
.performance-presets {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 15px;
}
.preset-button {
padding: 8px;
border: 1px solid #444;
border-radius: 6px;
background: rgba(255, 255, 255, 0.05);
color: white;
cursor: pointer;
font-size: 12px;
transition: all 0.3s ease;
}
.preset-button:hover {
border-color: #00ff88;
}
.preset-button.active {
border-color: #00ff88;
background: rgba(0, 255, 136, 0.2);
}
.control-group {
margin-bottom: 15px;
}
.control-group label {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
color: #ccc;
font-size: 14px;
}
.control-group input[type="range"] {
width: 100%;
height: 6px;
background: #444;
border-radius: 3px;
outline: none;
opacity: 0.7;
transition: opacity 0.2s;
}
.control-group input[type="range"]:hover {
opacity: 1;
}
.control-group input[type="range"]::-webkit-slider-thumb {
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #00ff88;
cursor: pointer;
box-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
}
.control-group input[type="checkbox"] {
width: 20px;
height: 20px;
accent-color: #00ff88;
}
.mode-select {
width: 100%;
padding: 8px;
border: 1px solid #444;
border-radius: 6px;
background: rgba(255, 255, 255, 0.1);
color: white;
font-size: 14px;
}
.touch-controls,
.rendering-optimizations,
.battery-optimizations {
margin-bottom: 15px;
}
.gesture-controls,
.optimization-toggles,
.thermal-controls {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
.performance-stats {
display: flex;
flex-direction: column;
gap: 8px;
}
.stat-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
font-size: 12px;
}
.stat-item span:first-child {
color: #ccc;
}
.stat-item span:last-child.good {
color: #00ff88;
}
.stat-item span:last-child.warning {
color: #ffaa00;
}
.stat-item span:last-child.bad {
color: #ff4444;
}
.touch-control-area {
position: absolute;
bottom: 20px;
left: 20px;
right: 20px;
display: flex;
justify-content: space-between;
align-items: flex-end;
pointer-events: none;
}
.virtual-joystick {
width: 120px;
height: 120px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.3);
pointer-events: auto;
touch-action: none;
}
.joystick-base {
width: 100%;
height: 100%;
position: relative;
}
.joystick-handle {
width: 50px;
height: 50px;
background: rgba(0, 255, 136, 0.8);
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: transform 0.1s ease;
box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
}
.action-buttons {
display: flex;
flex-direction: column;
gap: 15px;
pointer-events: auto;
}
.action-button {
padding: 15px 20px;
border: none;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
color: white;
font-size: 14px;
backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.3);
cursor: pointer;
transition: all 0.3s ease;
touch-action: manipulation;
}
.action-button:active {
background: rgba(0, 255, 136, 0.3);
transform: scale(0.95);
}
.action-button.jump {
background: rgba(255, 170, 0, 0.8);
}
.action-button.interact {
background: rgba(0, 170, 255, 0.8);
}
.gesture-hints {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
padding: 15px;
border-radius: 10px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
display: flex;
flex-direction: column;
gap: 10px;
}
.hint-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
color: #ccc;
}
.hint-icon {
font-size: 18px;
}
.performance-warning,
.battery-warning {
position: absolute;
top: 70px;
left: 50%;
transform: translateX(-50%);
background: rgba(255, 68, 68, 0.9);
padding: 10px 15px;
border-radius: 8px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
z-index: 1000;
}
.warning-content {
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
color: white;
}
.warning-icon {
font-size: 16px;
}
.optimize-button,
.power-save-button {
padding: 5px 10px;
border: none;
border-radius: 4px;
background: rgba(255, 255, 255, 0.2);
color: white;
cursor: pointer;
font-size: 12px;
transition: background 0.3s;
}
.optimize-button:hover,
.power-save-button:hover {
background: rgba(255, 255, 255, 0.3);
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.loading-content {
text-align: center;
color: white;
}
.mobile-loader {
margin-bottom: 30px;
}
.phone-frame {
width: 200px;
height: 400px;
background: #333;
border-radius: 20px;
padding: 20px;
position: relative;
margin: 0 auto;
border: 4px solid #555;
}
.screen-content {
width: 100%;
height: 100%;
background: #1a1a1a;
border-radius: 10px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 20px;
}
.loading-bar {
width: 80%;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
overflow: hidden;
}
.loading-bar::after {
content: '';
display: block;
width: 100%;
height: 100%;
background: #00ff88;
border-radius: 2px;
animation: loadingBar 2s infinite ease-in-out;
}
.loading-dots {
display: flex;
gap: 8px;
}
.dot {
width: 8px;
height: 8px;
background: #00ff88;
border-radius: 50%;
animation: loadingDots 1.4s infinite ease-in-out;
}
.dot:nth-child(1) { animation-delay: 0s; }
.dot:nth-child(2) { animation-delay: 0.2s; }
.dot:nth-child(3) { animation-delay: 0.4s; }
.loading-content h3 {
margin-bottom: 20px;
color: white;
font-size: 18px;
}
.loading-progress {
width: 300px;
margin: 0 auto 15px;
}
.progress-bar {
width: 100%;
height: 6px;
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
overflow: hidden;
margin-bottom: 10px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #00ff88, #00aaff);
border-radius: 3px;
transition: width 0.3s ease;
}
.optimization-stats {
display: flex;
flex-direction: column;
gap: 8px;
font-size: 12px;
color: #ccc;
}
.debug-overlay {
position: absolute;
top: 60px;
left: 10px;
background: rgba(0, 0, 0, 0.8);
padding: 10px;
border-radius: 6px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.debug-info {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 11px;
color: #ccc;
}
.quick-actions {
position: absolute;
bottom: 20px;
right: 20px;
display: flex;
flex-direction: column;
gap: 10px;
}
.quick-button {
width: 50px;
height: 50px;
border: none;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
color: white;
font-size: 18px;
backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.3);
cursor: pointer;
transition: all 0.3s ease;
display: flex;
justify-content: center;
align-items: center;
}
.quick-button:hover {
background: rgba(255, 255, 255, 0.2);
}
.quick-button.active {
background: rgba(0, 255, 136, 0.3);
border-color: #00ff88;
}
@keyframes loadingBar {
0% { transform: translateX(-100%); }
50% { transform: translateX(0); }
100% { transform: translateX(100%); }
}
@keyframes loadingDots {
0%, 80%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
40% {
transform: scale(1.2);
opacity: 1;
}
}
/* 移动端响应式设计 */
@media (max-width: 768px) {
.mobile-controls {
width: 280px;
right: 10px;
top: 10px;
}
.controls-content {
padding: 10px;
}
.performance-presets {
grid-template-columns: 1fr;
}
.touch-control-area {
bottom: 10px;
left: 10px;
right: 10px;
}
.virtual-joystick {
width: 100px;
height: 100px;
}
.joystick-handle {
width: 40px;
height: 40px;
}
.action-button {
padding: 12px 16px;
font-size: 12px;
}
.quick-actions {
bottom: 10px;
right: 10px;
}
.quick-button {
width: 45px;
height: 45px;
font-size: 16px;
}
}
@media (max-width: 480px) {
.mobile-controls {
width: 250px;
}
.virtual-joystick {
width: 80px;
height: 80px;
}
.joystick-handle {
width: 35px;
height: 35px;
}
.action-button {
padding: 10px 14px;
font-size: 11px;
}
.gesture-hints {
font-size: 12px;
padding: 10px;
}
}
/* 防止文本选择 */
.no-select {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* 改善触控反馈 */
@media (hover: none) and (pointer: coarse) {
.preset-button:active,
.action-button:active,
.quick-button:active {
transform: scale(0.95);
transition: transform 0.1s ease;
}
}
</style>
高级移动端优化技术
自适应性能管理系统
javascript
// 自适应性能管理器
class AdaptivePerformanceManager {
constructor() {
this.performanceTiers = this.detectPerformanceTier();
this.qualityLevels = {
low: this.createLowQualityConfig(),
medium: this.createMediumQualityConfig(),
high: this.createHighQualityConfig(),
ultra: this.createUltraQualityConfig()
};
this.currentConfig = this.qualityLevels[this.performanceTiers];
this.performanceHistory = [];
this.adaptationEnabled = true;
}
// 检测性能等级
detectPerformanceTier() {
const score = this.calculatePerformanceScore();
if (score >= 80) return 'ultra';
if (score >= 60) return 'high';
if (score >= 40) return 'medium';
return 'low';
}
// 计算性能分数
calculatePerformanceScore() {
let score = 0;
// GPU 性能评估
const gpuScore = this.evaluateGPUPerformance();
// CPU 性能评估
const cpuScore = this.evaluateCPUPerformance();
// 内存评估
const memoryScore = this.evaluateMemory();
// 设备类型加分
const deviceBonus = this.getDeviceBonus();
score = (gpuScore * 0.5) + (cpuScore * 0.3) + (memoryScore * 0.2) + deviceBonus;
return Math.min(100, Math.max(0, score));
}
// 评估GPU性能
evaluateGPUPerformance() {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) return 30;
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
if (debugInfo) {
const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
return this.scoreGPUByRenderer(renderer);
}
// 基于支持的扩展评估
return this.scoreGPUByExtensions(gl);
}
// 根据渲染器评分GPU
scoreGPUByRenderer(renderer) {
const highEndGPUs = [
'adreno', 'mali-g', 'apple gpu', 'nvidia', 'amd radeon'
];
const midRangeGPUs = [
'mali-t', 'powervr', 'intel hd', 'adreno 5'
];
const rendererLower = renderer.toLowerCase();
if (highEndGPUs.some(gpu => rendererLower.includes(gpu))) {
return 80;
} else if (midRangeGPUs.some(gpu => rendererLower.includes(gpu))) {
return 60;
} else {
return 40;
}
}
// 根据扩展评分GPU
scoreGPUByExtensions(gl) {
let score = 30;
// 检查支持的扩展
const extensions = [
'EXT_texture_filter_anisotropic',
'OES_texture_float',
'WEBGL_compressed_texture_etc',
'WEBGL_compressed_texture_astc'
];
const supportedExtensions = extensions.filter(ext => gl.getExtension(ext));
score += supportedExtensions.length * 10;
return Math.min(80, score);
}
// 评估CPU性能
evaluateCPUPerformance() {
// 通过简单基准测试评估CPU性能
const startTime = performance.now();
let operations = 0;
// 执行一些数学运算
for (let i = 0; i < 1000000; i++) {
operations += Math.sqrt(i) * Math.sin(i);
}
const endTime = performance.now();
const duration = endTime - startTime;
// 基于执行时间评分
if (duration < 50) return 80;
if (duration < 100) return 60;
if (duration < 200) return 40;
return 20;
}
// 评估内存
evaluateMemory() {
// 检查设备内存(近似值)
if (navigator.deviceMemory) {
return navigator.deviceMemory * 20; // 1GB = 20分, 8GB = 100分
}
// 基于用户代理的启发式评估
return this.estimateMemoryFromUA();
}
// 从用户代理估算内存
estimateMemoryFromUA() {
const ua = navigator.userAgent;
if (/iPhone.*OS 1[3-9]/.test(ua)) return 80; // 较新iPhone
if (/iPad.*OS 1[3-9]/.test(ua)) return 70; // 较新iPad
if (/Android 1[0-9]/.test(ua)) return 60; // 较新Android
if (/iPhone|iPad/.test(ua)) return 50; // 其他iOS设备
if (/Android/.test(ua)) return 40; // 其他Android设备
return 70; // 桌面设备默认较高分数
}
// 获取设备加分
getDeviceBonus() {
const ua = navigator.userAgent;
if (/iPhone1[3-9]/.test(ua)) return 10; // iPhone 12及更新
if (/iPad[8-9]/.test(ua)) return 10; // 较新iPad
if (/SM-G99/.test(ua)) return 8; // 三星旗舰
return 0;
}
// 创建低质量配置
createLowQualityConfig() {
return {
resolutionScale: 0.5,
shadowEnabled: false,
antialias: false,
textureQuality: 'low',
maxLights: 2,
maxParticles: 50,
lodBias: 2.0,
physicsQuality: 'low'
};
}
// 创建中等质量配置
createMediumQualityConfig() {
return {
resolutionScale: 0.75,
shadowEnabled: true,
shadowQuality: 'low',
antialias: false,
textureQuality: 'medium',
maxLights: 4,
maxParticles: 100,
lodBias: 1.5,
physicsQuality: 'medium'
};
}
// 创建高质量配置
createHighQualityConfig() {
return {
resolutionScale: 1.0,
shadowEnabled: true,
shadowQuality: 'medium',
antialias: true,
textureQuality: 'high',
maxLights: 8,
maxParticles: 200,
lodBias: 1.0,
physicsQuality: 'high'
};
}
// 创建超高质量配置
createUltraQualityConfig() {
return {
resolutionScale: 1.0,
shadowEnabled: true,
shadowQuality: 'high',
antialias: true,
textureQuality: 'ultra',
maxLights: 16,
maxParticles: 500,
lodBias: 0.5,
physicsQuality: 'ultra'
};
}
// 动态调整配置
adaptBasedOnPerformance(currentFPS, targetFPS) {
if (!this.adaptationEnabled) return;
this.performanceHistory.push(currentFPS);
if (this.performanceHistory.length > 60) {
this.performanceHistory.shift();
}
const averageFPS = this.performanceHistory.reduce((a, b) => a + b) / this.performanceHistory.length;
if (averageFPS < targetFPS * 0.8) {
// 性能不足,降低质量
this.downgradeQuality();
} else if (averageFPS > targetFPS * 1.2 && this.canUpgradeQuality()) {
// 性能充足,提升质量
this.upgradeQuality();
}
}
// 降低质量
downgradeQuality() {
const tiers = ['ultra', 'high', 'medium', 'low'];
const currentIndex = tiers.indexOf(this.performanceTiers);
if (currentIndex < tiers.length - 1) {
this.performanceTiers = tiers[currentIndex + 1];
this.currentConfig = this.qualityLevels[this.performanceTiers];
console.log(`性能适配: 降低到 ${this.performanceTiers} 质量`);
}
}
// 提升质量
upgradeQuality() {
const tiers = ['ultra', 'high', 'medium', 'low'];
const currentIndex = tiers.indexOf(this.performanceTiers);
if (currentIndex > 0) {
this.performanceTiers = tiers[currentIndex - 1];
this.currentConfig = this.qualityLevels[this.performanceTiers];
console.log(`性能适配: 提升到 ${this.performanceTiers} 质量`);
}
}
// 检查是否可以提升质量
canUpgradeQuality() {
const tiers = ['ultra', 'high', 'medium', 'low'];
const currentIndex = tiers.indexOf(this.performanceTiers);
return currentIndex > 0;
}
// 获取当前配置
getCurrentConfig() {
return this.currentConfig;
}
// 启用/禁用自适应
setAdaptationEnabled(enabled) {
this.adaptationEnabled = enabled;
}
}
注意事项与最佳实践
-
性能优化策略
- 优先考虑60FPS的流畅体验
- 使用适当的纹理压缩格式(ASTC、ETC2)
- 实现动态分辨率缩放
- 监控内存使用和温度
-
触控交互设计
- 提供足够的触控目标(最小44px)
- 实现手势反馈和视觉提示
- 支持多指触控和复杂手势
- 优化触摸响应时间
-
电池续航优化
- 在后台时暂停渲染
- 降低非活动标签页的更新频率
- 使用requestAnimationFrame进行节流
- 监控电池状态并调整画质
-
跨平台兼容性
- 测试不同设备和浏览器
- 提供优雅的性能降级
- 支持多种屏幕比例和分辨率
- 处理方向变化和键盘弹出
下一节预告
第38节:WebGL 2.0与Three.js新特性
将深入探索WebGL 2.0的核心功能,包括:计算着色器、变换反馈、多重渲染目标、实例化渲染,以及Three.js对这些新特性的支持实现。