Files
BlackFruit-UI/js/globe.js
yiqiu 9c7dd5ab9b
All checks were successful
continuous-integration/drone/push Build is passing
达到
2025-11-21 22:14:00 +08:00

226 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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, 80, 80);
var earthMaterial = new THREE.MeshPhongMaterial({
color: new THREE.Color("#0a1024"), // 深蓝底色
emissive: new THREE.Color("#050814"), // 微弱自发光
specular: new THREE.Color("#3aaefc"), // 高光偏蓝
shininess: 55,
transparent: false,
opacity: 1,
});
var earthMesh = new THREE.Mesh(earthGeometry, earthMaterial);
globe.add(earthMesh);
// 经纬线网格wireframe增加科技感
var gridGeometry = new THREE.SphereGeometry(
earthRadius * 1.002,
32,
32
);
var gridMaterial = new THREE.MeshBasicMaterial({
color: 0x2f7fff,
wireframe: true,
transparent: true,
opacity: 0.35,
});
var gridMesh = new THREE.Mesh(gridGeometry, gridMaterial);
globe.add(gridMesh);
// 赤道光环
var ringGeometry = new THREE.RingGeometry(
earthRadius * 1.05,
earthRadius * 1.25,
64
);
var ringMaterial = new THREE.MeshBasicMaterial({
color: 0x2f7fff,
transparent: true,
opacity: 0.22,
side: THREE.DoubleSide,
});
var ringMesh = new THREE.Mesh(ringGeometry, ringMaterial);
ringMesh.rotation.x = Math.PI / 2; // 放到赤道平面
globe.add(ringMesh);
} 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();
}
})();