// 3D 地球效果(首页 banner 右侧) // 依赖:Three.js(全局 THREE 对象),脚本路径建议:/web/BlackFruit-web/vender/three/three.min.js (function () { function initGlobe() { var container = document.getElementById("bannerGlobe"); if (!container) { console.warn("[Globe] bannerGlobe container not found"); return; } if (typeof THREE === "undefined") { // Three.js 未加载时直接跳过,不影响其它功能 console.warn("[Globe] THREE is undefined, please ensure three.min.js is loaded"); return; } var width = container.clientWidth || 400; var height = container.clientHeight || 400; var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000); var radius = 1.2; camera.position.set(0, 0, radius * 3.2); camera.lookAt(0, 0, 0); var renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); renderer.setSize(width, height); renderer.setClearColor(0x000000, 0); // 透明背景,融入页面 container.appendChild(renderer.domElement); // 光照:柔和的环境光 + 方向光 scene.add(new THREE.AmbientLight(0xffffff, 0.5)); var dirLight = new THREE.DirectionalLight(0xffffff, 0.9); dirLight.position.set(5, 3, 5); scene.add(dirLight); var globeGroup = new THREE.Group(); scene.add(globeGroup); // 地球球体 var earthGeometry = new THREE.SphereGeometry(radius, 64, 64); // 纯色高光材质,若后续有贴图可替换为 MeshPhongMaterial + map var earthMaterial = new THREE.MeshPhongMaterial({ color: 0x163a5f, emissive: 0x061727, shininess: 35, specular: 0x3aaefc, }); var earthMesh = new THREE.Mesh(earthGeometry, earthMaterial); globeGroup.add(earthMesh); // 大气层发光效果 var atmosphereGeometry = new THREE.SphereGeometry(radius * 1.06, 64, 64); var atmosphereMaterial = new THREE.MeshBasicMaterial({ color: 0x2b9fff, transparent: true, opacity: 0.25, side: THREE.BackSide, }); var atmosphereMesh = new THREE.Mesh( atmosphereGeometry, atmosphereMaterial ); globeGroup.add(atmosphereMesh); // 将经纬度转换为球面坐标 function latLngToVector3(lat, lng, r) { var phi = (90 - lat) * (Math.PI / 180); var theta = (lng + 180) * (Math.PI / 180); var x = -r * Math.sin(phi) * Math.cos(theta); var z = r * Math.sin(phi) * Math.sin(theta); var y = r * Math.cos(phi); return new THREE.Vector3(x, y, z); } // 一些示例节点(可以根据业务改为真实机房经纬度) var nodeConfigs = [ { lat: 39.9, lng: 116.4 }, // 北京 { lat: 31.2, lng: 121.5 }, // 上海 { lat: 22.5, lng: 114.1 }, // 香港 { lat: 1.3, lng: 103.8 }, // 新加坡 { lat: 37.8, lng: -122.4 }, // 旧金山 { lat: 52.5, lng: 13.4 }, // 柏林 { lat: 35.7, lng: 139.7 }, // 东京 ]; var nodeGroup = new THREE.Group(); globeGroup.add(nodeGroup); var nodeMaterial = new THREE.MeshBasicMaterial({ color: 0x5ad8ff, }); var nodes = []; var nodeRadius = radius * 0.03; nodeConfigs.forEach(function (cfg, index) { var pos = latLngToVector3(cfg.lat, cfg.lng, radius * 1.02); var geom = new THREE.SphereGeometry(nodeRadius, 16, 16); var mesh = new THREE.Mesh(geom, nodeMaterial); mesh.position.copy(pos); nodeGroup.add(mesh); nodes.push({ mesh: mesh, baseScale: 1, phase: Math.random() * Math.PI * 2 + index, }); }); // 通过 Points 实现星空点阵背景 var starGeometry = new THREE.BufferGeometry(); var starCount = 600; var starPositions = new Float32Array(starCount * 3); for (var i = 0; i < starCount; i++) { var sr = radius * 4 + Math.random() * radius * 2; var theta = Math.random() * Math.PI * 2; var phi = Math.acos(2 * Math.random() - 1); var sx = sr * Math.sin(phi) * Math.cos(theta); var sy = sr * Math.sin(phi) * Math.sin(theta); var sz = sr * Math.cos(phi); starPositions[i * 3] = sx; starPositions[i * 3 + 1] = sy; starPositions[i * 3 + 2] = sz; } starGeometry.setAttribute( "position", new THREE.BufferAttribute(starPositions, 3) ); var starMaterial = new THREE.PointsMaterial({ color: 0x3f76ff, size: 0.02, transparent: true, opacity: 0.8, }); var stars = new THREE.Points(starGeometry, starMaterial); scene.add(stars); // 自适应窗口大小 function onResize() { if (!container) return; var w = container.clientWidth || width; var h = container.clientHeight || height; camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); } window.addEventListener("resize", onResize); // 动画循环 var lastTime = performance.now(); function animate() { requestAnimationFrame(animate); var now = performance.now(); var delta = (now - lastTime) / 1000; lastTime = now; // 地球缓慢自转 globeGroup.rotation.y += delta * 0.25; // 星辰慢速旋转,增强空间感 stars.rotation.y -= delta * 0.05; // 节点呼吸效果 var t = now / 1000; nodes.forEach(function (n) { var s = n.baseScale * (1 + 0.6 * Math.sin(t * 2.5 + n.phase)); n.mesh.scale.set(s, s, s); }); renderer.render(scene, camera); } animate(); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initGlobe); } else { initGlobe(); } })();