// 3D 地球效果(首页 banner 右侧)基于 three-globe // 依赖:全局 THREE 和 ThreeGlobe(在 index.html 中通过 CDN 引入) (function () { function initGlobe() { var container = document.getElementById("bannerGlobe"); if (!container) { console.warn("[Globe] bannerGlobe container not found"); return; } if (typeof THREE === "undefined") { console.warn( "[Globe] THREE is undefined, please ensure three.min.js is loaded" ); return; } if (typeof ThreeGlobe === "undefined") { console.warn( "[Globe] ThreeGlobe is undefined, please ensure three-globe.min.js is loaded" ); return; } var width = container.clientWidth || 400; var height = container.clientHeight || 400; // 基础 Three.js 场景 var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000); // 稍微偏右俯视一点 camera.position.set(0, 0.4, 4.0); 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); // 柔和光照 var ambient = new THREE.AmbientLight(0xffffff, 0.65); scene.add(ambient); var dirLight = new THREE.DirectionalLight(0xffffff, 0.9); dirLight.position.set(5, 3, 5); scene.add(dirLight); // 示例节点数据(可后续替换为真实机房经纬度) var nodes = [ { name: "Beijing", lat: 39.9, lng: 116.4 }, { name: "Shanghai", lat: 31.2, lng: 121.5 }, { name: "Hong Kong", lat: 22.5, lng: 114.1 }, { name: "Singapore", lat: 1.3, lng: 103.8 }, { name: "San Francisco", lat: 37.8, lng: -122.4 }, { name: "Berlin", lat: 52.5, lng: 13.4 }, { name: "Tokyo", lat: 35.7, lng: 139.7 }, ]; // 示例 arcs:从北京发出到其他节点 var arcs = nodes .filter(function (n) { return n.name !== "Beijing"; }) .map(function (n) { return { startLat: 39.9, startLng: 116.4, endLat: n.lat, endLng: n.lng, }; }); // three-globe 实例(仅使用其数据层,大气层和坐标系) var globe = new ThreeGlobe({ waitForGlobeReady: true, animateIn: true, }) // 隐藏 three-globe 默认球体,我们自己画一个实心地球 .showGlobe(false) .showAtmosphere(true) .atmosphereColor("#2b9fff") .atmosphereAltitude(0.18) .showGraticules(false) // 节点柱状体 .pointsData(nodes) .pointColor(function () { return "#5ad8ff"; }) .pointAltitude(0.06) .pointRadius(0.32) .pointsTransitionDuration(1000) // 弧线 .arcsData(arcs) .arcColor(function () { return ["#5ad8ff", "#ffffff"]; }) .arcAltitude(0.35) .arcStroke(0.45) .arcDashLength(0.6) .arcDashGap(0.25) .arcDashAnimateTime(2600); scene.add(globe); // 自绘一个实心地球球体,避免默认球体材质透明或贴图问题 try { var earthRadius = 1; // three-globe 使用的半径单位 var earthGeometry = new THREE.SphereGeometry(earthRadius, 64, 64); var earthMaterial = new THREE.MeshPhongMaterial({ color: new THREE.Color("#163a5f"), // 基础色:深蓝 emissive: new THREE.Color("#040915"), // 自发光:微亮 specular: new THREE.Color("#3aaefc"), // 高光:亮蓝 shininess: 40, transparent: false, opacity: 1, }); var earthMesh = new THREE.Mesh(earthGeometry, earthMaterial); // 将地球球体挂到 three-globe 对象上,保证与节点/弧线共用坐标系 globe.add(earthMesh); } catch (e) { console.warn("[Globe] failed to create custom earth sphere:", e); } // 暴露给调试用 if (typeof window !== "undefined") { window.__globe = globe; } // 星辰背景(简单加一点点空间感) var starGeometry = new THREE.BufferGeometry(); var starCount = 600; var starPositions = new Float32Array(starCount * 3); for (var i = 0; i < starCount; i++) { var sr = 10 + Math.random() * 5; 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.08, transparent: true, opacity: 0.7, }); 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; // 地球自转 globe.rotation.y += delta * 0.25; // 星空轻微旋转 stars.rotation.y -= delta * 0.03; renderer.render(scene, camera); } animate(); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initGlobe); } else { initGlobe(); } })();