// 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(40, width / height, 0.1, 1000); // 稍微偏右俯视一点,拉远一点避免球体被裁剪 camera.position.set(0, 0.3, 4.8); 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 内置球体贴图,只保留大气层,陆地轮廓由 polygons 层绘制 .showGlobe(false) .showAtmosphere(false) .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); // 按 hollow-globe 风格加载陆地多边形,绘制空心地球轮廓 (function loadLandPolygons() { if (typeof topojson === "undefined") { console.warn( "[Globe] topojson-client is undefined, unable to render hollow globe polygons" ); return; } // 请确保 /web/BlackFruit-web/assets/data/land-110m.json 存在, // 内容为你刚才提供的 Topology JSON。 fetch("/web/BlackFruit-web/assets/data/land-110m.json") .then(function (res) { return res.json(); }) .then(function (landTopo) { try { var landGeo = topojson.feature( landTopo, landTopo.objects.land ).features; globe .polygonsData(landGeo) .polygonCapMaterial( new THREE.MeshLambertMaterial({ color: "darkslategrey", side: THREE.DoubleSide, }) ) .polygonSideColor(function () { return "rgba(0,0,0,0)"; }); } catch (e) { console.warn("[Globe] failed to parse land-110m topology:", e); } }) .catch(function (err) { console.warn( "[Globe] failed to load /web/BlackFruit-web/assets/data/land-110m.json:", err ); }); })(); // 暴露给调试用 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(); } })();