3D地球可视化教程 - 第3篇:地球动画与相机控制
难度: ⭐⭐⭐☆☆ 中级
预计时间: 40分钟
技术栈: Three.js + GSAP + 数学几何
📖 教程简介
在前两篇教程中,我们创建了基础地球渲染和夜晚纹理效果。现在,我们要让地球"动起来"!
本篇将深入学习动画系统的设计和实现,包括炫酷的地球入场动画、智能的相机控制,以及自动旋转功能。
本篇学习目标
- 掌握GSAP动画库的使用
- 理解贝塞尔曲线的数学原理
- 实现复杂的相机动画序列
- 学习自动旋转系统设计
- 掌握动画时序控制
最终效果预览
完成本篇教程后,你将获得:
- 地球入场动画
- 弧形路径的平滑运动
- 自动旋转功能
- 动画时序控制
动画理论基础
为什么需要动画系统?
静态的3D场景虽然美观,但缺乏吸引力。动画系统的价值:
- 吸引注意力 - 动态效果更容易抓住用户眼球
- 提升用户体验 - 平滑的过渡比突变更舒适
- 增强视觉层次 - 通过运动展示空间关系
- 营造氛围 - 动画传达情感和风格
动画系统架构
地球动画系统
├── GSAP时间线管理 → 控制动画序列和时序
├── 贝塞尔曲线路径 → 创建自然的运动轨迹
├── 相机动画控制 → 管理视角变化
└── 自动旋转系统 → 持续的背景动画
核心动画组件解析
1. GSAP时间线系统
GSAP (GreenSock Animation Platform) 是业界领先的动画库:
// CameraAnimationController.js - GSAP时间线
const timeline = gsap.timeline({onStart: () => {this.isAnimating = true;if (onStart) onStart();},onComplete: () => {this.isAnimating = false; if (onComplete) onComplete();},
});// 旋转动画(立即开始)
timeline.to(animationData, {rotationProgress: 1,duration: duration / 1000, // GSAP使用秒为单位ease: "power2.inOut", // 缓动函数onUpdate: () => {// 四元数插值实现平滑旋转const currentQuaternion = new THREE.Quaternion();currentQuaternion.slerpQuaternions(startQuaternion, targetQuaternion, animationData.rotationProgress);earthGroup.rotation.setFromQuaternion(currentQuaternion);}
}, 0);// 位移动画(延迟开始)
timeline.to(animationData, {positionProgress: 1,duration: duration / 1000,ease: "power2.inOut",onUpdate: () => {// 贝塞尔曲线插值实现弧形路径const currentPosition = this.calculateQuadraticBezier(startPosition, midPoint, targetPosition, animationData.positionProgress);earthGroup.position.copy(currentPosition);}
}, positionDelay / 1000); // 延迟500ms开始
GSAP的优势:
- 高性能 - 优化的渲染循环
- 易用性 - 直观的API设计
- 功能丰富 - 支持复杂的动画序列
- 兼容性好 - 跨浏览器支持
贝塞尔曲线数学原理
二次贝塞尔曲线
地球的入场动画使用二次贝塞尔曲线创建弧形路径:
/*** 二次贝塞尔曲线公式* B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂* * 其中:* - P₀: 起始点* - P₁: 控制点(决定弧形方向)* - P₂: 结束点* - t: 参数 [0, 1]*/
calculateQuadraticBezier(p0, p1, p2, t) {const oneMinusT = 1 - t;const tSquared = t * t;const oneMinusTSquared = oneMinusT * oneMinusT;// 分别计算 x, y, z 坐标result.x = oneMinusTSquared * p0.x + 2 * oneMinusT * t * p1.x + tSquared * p2.x;result.y = oneMinusTSquared * p0.y + 2 * oneMinusT * t * p1.y + tSquared * p2.y;result.z = oneMinusTSquared * p0.z + 2 * oneMinusT * t * p1.z + tSquared * p2.z;return result;
}
路径控制参数
// 动画路径配置
const pathConfig = {startPosition: new THREE.Vector3(3.8, 0, 17.4), // 起始位置(远距离)targetPosition: new THREE.Vector3(-0.8, -5, -7), // 目标位置(近距离)arcHeight: -20, // 弧形高度(负值向下弯曲)midPointPosition: 0.2, // 中间点位置比例positionDelay: 500, // 位移延迟(毫秒)
};
参数作用解释:
- arcHeight: 控制弧形的弯曲程度,负值创建向下的弧形
- midPointPosition: 控制弧形的对称性,0.2表示偏向起点
- positionDelay: 让旋转先开始,位移后开始,创建层次感
自动旋转系统
旋转控制逻辑
// EarthMouseController.js - 自动旋转
update() {if (!this.isMouseDown) {// 🔄 自动旋转功能 ✅ 第3篇教程if (this.enableAutoRotate) {this.elementsGroup.rotation.y += this.autoRotateSpeed;}}
}
旋转参数配置
// 旋转系统参数
const rotationConfig = {autoRotateSpeed: 0.001, // 自动旋转速度 (弧度/帧)enableAutoRotate: true, // 是否启用自动旋转rotationSpeed: 0.005, // 手动旋转速度dampingFactor: 0.95, // 阻尼系数 (第7篇教程)
};
速度对比:
- 0.001 弧度/帧 ≈ 3.6度/秒 ≈ 1圈/100秒
- 这个速度既能看到旋转效果,又不会太快导致眩晕
动画序列设计
分层动画时序
我们的地球动画采用分层时序设计:
// Scene.js - 动画触发时序
const animationSequence = {// 阶段1: 地球入场 (0% - 90%)earthAnimation: {rotation: "立即开始", // 旋转动画position: "延迟500ms开始", // 位移动画},// 阶段2: 圆环效果 (90%时触发)ringAnimation: {trigger: "地球动画90%时",effect: "圆环从0缩放到1",},// 阶段3: 飞线效果 (90%时触发) flightLineAnimation: {trigger: "地球动画90%时",effect: "飞线依次绘制",},// 阶段4: UI界面 (95%时触发)uiAnimation: {trigger: "地球动画95%时", effect: "界面元素滑入",}
};
时序控制实现
// Scene.js - 动画进度监控
onUpdate: (progress, easedProgress, data) => {// 🪐 90%时触发圆环动画if (!this.ringAnimationTriggered && progress >= 0.9) {this.ringAnimationTriggered = true;this.startRingScaleAnimation();}// ⚡ 90%时触发飞线动画if (!this.flightLineAnimationTriggered && progress >= 0.9) {this.flightLineAnimationTriggered = true;this.startFlightLineAnimations();}// 🎨 95%时触发UI动画if (!this.uiAnimationTriggered && progress >= 0.95) {this.uiAnimationTriggered = true;this.dispatchEvent("uiAnimationTrigger");}
}
相机动画系统
相机控制器设计
class CameraAnimationController {constructor(camera, controls) {this.camera = camera;this.controls = controls;// 预设的相机位置this.presetPositions = {close: new THREE.Vector3(2.7, 19.4, 12.6), // 近景位置far: new THREE.Vector3(6.6, 23.5, 33.8), // 远景位置};}// 相机拉远动画zoomOutWithPresetPositions(options = {}) {const { duration = 3000, onComplete } = options;gsap.to(this.camera.position, {x: this.presetPositions.far.x,y: this.presetPositions.far.y,z: this.presetPositions.far.z,duration: duration / 1000,ease: "power2.out",onComplete: onComplete});}
}
核心技术实现
1. 四元数旋转插值
// 使用四元数实现平滑旋转
const startQuaternion = new THREE.Quaternion().setFromEuler(startRotation);
const targetQuaternion = new THREE.Quaternion().setFromEuler(targetRotation);
const currentQuaternion = new THREE.Quaternion();// SLERP (球面线性插值)
currentQuaternion.slerpQuaternions(startQuaternion, targetQuaternion, progress
);// 应用到地球组
earthGroup.rotation.setFromQuaternion(currentQuaternion);
四元数的优势:
- 避免万向锁问题
- 插值结果更平滑
- 计算效率更高
- 旋转路径最短
2. 弧形路径算法
// 计算弧形中间点
const midPoint = new THREE.Vector3();
midPoint.lerpVectors(startPosition, targetPosition, midPointPosition);
midPoint.y += arcHeight; // 向上/向下偏移创建弧形// 贝塞尔曲线插值
const currentPosition = calculateQuadraticBezier(startPosition, // P₀: 起点midPoint, // P₁: 控制点targetPosition, // P₂: 终点progress // t: [0, 1]
);
3. 动画时序管理
// 复杂动画的时序控制
const timeline = gsap.timeline();// 同时开始的动画
timeline.to(target1, { /* 动画1 */ }, 0);
timeline.to(target2, { /* 动画2 */ }, 0);// 延迟开始的动画
timeline.to(target3, { /* 动画3 */ }, 0.5); // 延迟0.5秒// 动画完成后的动画
timeline.to(target4, { /* 动画4 */ }, "+=0.2"); // 上个动画完成后0.2秒
动画参数详解
地球动画配置
// Scene.js - 地球动画参数
this.earthAnimationParams = {arcHeight: -20, // 弧形高度偏移量midPointPosition: 0.2, // 中间点位置比例positionDelay: 500, // 位移动画延迟时间(ms)duration: 2500, // 动画持续时间(ms)// 其他动画触发时机ringAnimationTriggerProgress: 0.9, // 90%时触发圆环flightLineAnimationTriggerProgress: 0.9, // 90%时触发飞线uiAnimationTriggerProgress: 0.95, // 95%时触发UI
};
参数调节技巧:
- arcHeight < 0: 向下弯曲的弧形,更有重力感
- midPointPosition < 0.5: 偏向起点的弧形,加速感更强
- positionDelay > 0: 先旋转后位移,层次感更好
自动旋转配置
// EarthMouseController.js - 旋转参数
const rotationParams = {enableAutoRotate: true, // 启用自动旋转autoRotateSpeed: 0.001, // 旋转速度 (弧度/帧)rotationSpeed: 0.005, // 手动旋转速度// 旋转限制enableVerticalLimit: true, // 启用垂直限制maxVerticalAngle: Math.PI / 3, // 最大角度 (60°)minVerticalAngle: -Math.PI / 3, // 最小角度 (-60°)
};
视觉效果分析
入场动画效果
-
阶段1 (0-500ms): 地球开始旋转
- 从倾斜角度旋转到正常角度
- 四元数插值保证平滑过渡
-
阶段2 (500-2500ms): 地球位移
- 从远处弧形飞入到目标位置
- 贝塞尔曲线创造自然的飞行轨迹
-
阶段3 (2250ms): 触发后续效果
- 90%进度时启动圆环和飞线
- 95%进度时启动UI动画
自动旋转效果
// 持续的背景动画
if (this.enableAutoRotate) {this.elementsGroup.rotation.y += this.autoRotateSpeed;
}
视觉特点:
- 持续旋转 - 保持画面活力
- 适中速度 - 既明显又不眩晕
- 可中断 - 用户交互时自动停止
实现技术详解
1. 事件驱动架构
// Scene.js - 事件系统
class Scene {// 事件监听器管理addEventListener(event, callback) {if (!this.eventListeners.has(event)) {this.eventListeners.set(event, []);}this.eventListeners.get(event).push(callback);}// 事件分发dispatchEvent(event, data) {if (this.eventListeners.has(event)) {this.eventListeners.get(event).forEach(callback => {callback(data);});}}
}
2. 动画状态管理
// 动画状态标记
this.ringAnimationTriggered = false;
this.flightLineAnimationTriggered = false;
this.uiAnimationTriggered = false;// 防止重复触发
if (!this.ringAnimationTriggered && progress >= 0.9) {this.ringAnimationTriggered = true;this.startRingScaleAnimation();
}
3. 缓动函数选择
// 不同阶段使用不同的缓动函数
const easingFunctions = {"power2.inOut": "平滑的加速和减速","power2.out": "快速开始,慢慢结束","elastic.out": "弹性效果","back.out": "回弹效果",
};
动画调节技巧
获得最佳动画效果
自然的入场动画
{duration: 2500, // 不急不慢的时长arcHeight: -20, // 向下弧形,符合重力midPointPosition: 0.2, // 偏向起点,加速感positionDelay: 500, // 先转后移,层次感
}
舒适的自动旋转
{autoRotateSpeed: 0.001, // 1圈约100秒enableAutoRotate: true, // 默认开启
}
实践操作
查看动画效果
刷新页面 (http://localhost:5174),你会看到:
-
** 入场动画** (前2.5秒)
- 地球从右上方弧形飞入
- 同时进行旋转调整
- 平滑过渡到最终位置
-
** 自动旋转** (动画完成后)
- 地球开始缓慢自动旋转
- 鼠标拖拽时暂停自动旋转
- 释放鼠标后恢复自动旋转
动画参数实验
你可以尝试修改参数观察效果:
// 在Scene.js中修改这些值
this.earthAnimationParams = {arcHeight: -30, // 尝试 -10, -20, -30midPointPosition: 0.3, // 尝试 0.1, 0.2, 0.5duration: 3000, // 尝试 2000, 3000, 4000positionDelay: 800, // 尝试 300, 500, 800
};
技术深度分析
1. 动画性能优化
// 使用requestAnimationFrame确保流畅度
animate() {this.animationId = requestAnimationFrame(this.animate.bind(this));// 只在需要时更新if (this.lights) this.lights.update();if (this.earthNight) this.earthNight.update(lightPos);this.controls.update();this.renderer.render(this.scene, this.camera);
}
2. 内存管理
// 动画完成后的清理
onComplete: () => {this.isAnimating = false;// 重置动画状态this.ringAnimationTriggered = false;this.flightLineAnimationTriggered = false;this.uiAnimationTriggered = false;
}
3. 数学优化
// 预计算常用值
const oneMinusT = 1 - t;
const tSquared = t * t;
const oneMinusTSquared = oneMinusT * oneMinusT;// 避免重复计算
result.x = oneMinusTSquared * p0.x + 2 * oneMinusT * t * p1.x + tSquared * p2.x;
本篇总结
** 核心知识点**
-
GSAP动画库
- 时间线管理
- 缓动函数
- 动画序列控制
-
数学几何
- 贝塞尔曲线原理
- 四元数旋转插值
- 弧形路径计算
-
动画设计
- 分层时序控制
- 视觉层次营造
- 用户体验优化
-
系统架构
- 事件驱动设计
- 状态管理
- 性能优化
🎨 视觉效果成果
- 震撼入场 - 地球从天而降的视觉冲击
- 自然运动 - 符合物理直觉的弧形路径
- 持续活力 - 自动旋转保持画面生动
- 智能交互 - 用户操作时自动暂停
🚀 下一篇预告
在第4篇教程中,我们将学习:
☁️ 云层系统与纹理动画
- 动态纹理UV动画
- 透明度渐变效果
- 多层渲染技术
- 大气散射模拟
预期效果:
- ☁️ 缓慢飘动的云层
- 🌫️ 真实的大气效果
- 🎨 丰富的视觉层次
🎉 恭喜完成第3篇教程!
在下一篇中,我们将添加云层效果,让地球更加真实。
本文是《3D地球可视化教程系列》的第3篇。下一篇:《云层系统与纹理动画》