初始化多多畅职企业内推平台项目

功能特性:
- 3D地球动画与中国地图可视化
- 省份/城市/企业搜索功能
- 308家企业数据展示
- 响应式设计(PC端和移动端)
- 企业详情页面与业务板块展示
- 官网新闻轮播图

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
KQL
2025-11-22 19:38:14 +08:00
commit ab50931347
41 changed files with 56412 additions and 0 deletions

288
js/scene/SceneManager.js Normal file
View File

@@ -0,0 +1,288 @@
/* ===================================
3D场景管理器 - 主控制器
=================================== */
import { CONFIG } from '../config.js';
import { StarSystem } from './StarSystem.js';
import { EarthModel } from './EarthModel.js';
import { Transition } from './Transition.js';
export class SceneManager {
constructor(container, onTransitionComplete) {
this.container = container;
this.onTransitionComplete = onTransitionComplete;
// 状态标志
this.isIntro = true;
this.isHovering = false;
this.parallaxX = 0;
this.parallaxY = 0;
// Three.js核心对象
this.scene = null;
this.camera = null;
this.renderer = null;
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
// 子系统
this.starSystem = null;
this.earthModel = null;
this.transition = null;
this.init();
}
// 初始化场景
init() {
// 创建场景
this.scene = new THREE.Scene();
this.scene.fog = new THREE.FogExp2(CONFIG.scene.fog.color, CONFIG.scene.fog.density);
// 创建相机
const camConfig = CONFIG.scene.camera;
this.camera = new THREE.PerspectiveCamera(
camConfig.fov,
window.innerWidth / window.innerHeight,
camConfig.near,
camConfig.far
);
this.camera.position.z = camConfig.initialZ; // 开场动画:从深空开始
// 创建渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setClearColor(0x000000, 1); // 设置黑色背景
this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.container.appendChild(this.renderer.domElement);
// 创建星空系统
this.starSystem = new StarSystem(this.scene);
// 创建地球模型
this.earthModel = new EarthModel(this.scene);
// 添加灯光
this.setupLights();
// 绑定事件
this.bindEvents();
// 启动动画循环
this.animate();
}
// 设置灯光
setupLights() {
const lights = CONFIG.scene.lights;
// 环境光
this.scene.add(new THREE.AmbientLight(lights.ambient));
// 太阳光
const sunLight = new THREE.DirectionalLight(lights.sun.color, lights.sun.intensity);
sunLight.position.set(...lights.sun.position);
this.scene.add(sunLight);
// 边缘光
const rimLight = new THREE.SpotLight(lights.rim.color, lights.rim.intensity);
rimLight.position.set(...lights.rim.position);
rimLight.lookAt(0, 0, 0);
this.scene.add(rimLight);
}
// 开场动画
startIntroSequence(uiElements) {
const animConfig = CONFIG.animation.intro;
const tl = gsap.timeline({
onComplete: () => {
this.isIntro = false; // 动画结束,允许交互
}
});
// A. 摄像机缓慢推进 (从 z=100 -> z=16)
tl.to(this.camera.position, {
z: CONFIG.scene.camera.defaultZ,
duration: animConfig.cameraDuration,
ease: "power2.out"
});
// B. UI 淡入 (标题和提示依次出现)
tl.to('.v1-title', {
opacity: 1,
duration: 1.5,
ease: "power2.out"
}, `-=${animConfig.titleDelay}`);
tl.to('.v1-subtitle', {
opacity: 1,
duration: 1.2,
ease: "power2.out"
}, `-=${animConfig.subtitleDelay}`);
tl.to('.instruction-hint', {
opacity: 1,
duration: 1.0,
ease: "power2.out"
}, `-=${animConfig.hintDelay}`);
}
// 初始化转场系统
initTransition(uiElements) {
this.transition = new Transition(
this.camera,
this.earthModel,
uiElements,
this.onTransitionComplete
);
}
// 绑定事件
bindEvents() {
// 鼠标移动
window.addEventListener('mousemove', (e) => this.onMouseMove(e));
// 鼠标点击
window.addEventListener('mousedown', () => this.onMouseDown());
// 触摸移动(移动端)
window.addEventListener('touchmove', (e) => this.onTouchMove(e), { passive: false });
// 触摸点击(移动端)
window.addEventListener('touchstart', (e) => this.onTouchStart(e));
// 窗口resize
window.addEventListener('resize', () => this.onWindowResize());
}
// 鼠标移动事件
onMouseMove(e) {
if (this.transition && this.transition.isActive()) return;
this.mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
// 只有开场动画结束后才检测悬停
if (!this.isIntro) {
this.raycaster.setFromCamera(this.mouse, this.camera);
const intersects = this.raycaster.intersectObjects(
this.earthModel.getInteractiveObjects()
);
if (intersects.length > 0) {
if (!this.isHovering) {
this.isHovering = true;
this.container.classList.add('interactive');
gsap.to(this.earthModel.getAtmosphere().scale, {
x: 1.03,
y: 1.03,
z: 1.03,
duration: 0.3
});
}
} else {
if (this.isHovering) {
this.isHovering = false;
this.container.classList.remove('interactive');
gsap.to(this.earthModel.getAtmosphere().scale, {
x: 1,
y: 1,
z: 1,
duration: 0.3
});
}
}
}
// 视差效果
this.parallaxX = (e.clientX - window.innerWidth / 2) * 0.001;
this.parallaxY = (e.clientY - window.innerHeight / 2) * 0.001;
}
// 鼠标点击事件
onMouseDown() {
if (this.isIntro || !this.isHovering) return;
if (this.transition && this.transition.isActive()) return;
// 触发转场
if (this.transition) {
this.transition.trigger();
}
}
// 触摸移动事件(移动端)
onTouchMove(e) {
if (this.transition && this.transition.isActive()) return;
const touch = e.touches[0];
this.mouse.x = (touch.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(touch.clientY / window.innerHeight) * 2 + 1;
// 视差效果
this.parallaxX = (touch.clientX - window.innerWidth / 2) * 0.001;
this.parallaxY = (touch.clientY - window.innerHeight / 2) * 0.001;
}
// 触摸点击事件(移动端)
onTouchStart(e) {
if (this.isIntro) return;
if (this.transition && this.transition.isActive()) return;
const touch = e.touches[0];
this.mouse.x = (touch.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(touch.clientY / window.innerHeight) * 2 + 1;
// 检测是否点击地球
this.raycaster.setFromCamera(this.mouse, this.camera);
const intersects = this.raycaster.intersectObjects(
this.earthModel.getInteractiveObjects()
);
if (intersects.length > 0) {
// 触发转场
if (this.transition) {
this.transition.trigger();
}
}
}
// 窗口resize
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
// 动画循环
animate() {
requestAnimationFrame(() => this.animate());
// 如果不在转场中
if (!this.transition || !this.transition.isActive()) {
// 地球和云层旋转
this.earthModel.rotate();
// 只有开场动画结束后才启用视差效果
if (!this.isIntro) {
this.camera.position.x += (this.parallaxX * 8 - this.camera.position.x) * 0.05;
this.camera.position.y += (-this.parallaxY * 8 - this.camera.position.y) * 0.05;
this.camera.lookAt(0, 0, 0);
}
}
this.renderer.render(this.scene, this.camera);
}
// 销毁场景
dispose() {
if (this.starSystem) this.starSystem.dispose();
if (this.earthModel) this.earthModel.dispose();
if (this.renderer) {
this.renderer.dispose();
this.container.removeChild(this.renderer.domElement);
}
}
}