This commit is contained in:
@@ -68,6 +68,33 @@
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* 3D 地球容器:位于轮播图偏中右位置 */
|
||||
.banner-globe {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8%; /* 根据视觉调节,让地球更靠中间一点 */
|
||||
transform: translateY(-50%);
|
||||
width: 420px;
|
||||
height: 420px;
|
||||
pointer-events: none; /* 不影响轮播图滑动与点击 */
|
||||
}
|
||||
|
||||
/* 中等屏幕下缩小地球,避免挡住文案 */
|
||||
@media (max-width: 1400px) {
|
||||
.banner-globe {
|
||||
right: 4%;
|
||||
width: 360px;
|
||||
height: 360px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 小屏幕(如窄笔记本)下隐藏 3D 地球,保持可用性 */
|
||||
@media (max-width: 1100px) {
|
||||
.banner-globe {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.banner .banner-s {
|
||||
box-shadow: 0px 0px 16px rgba(52, 52, 52, 0.16);
|
||||
border-radius: 3px;
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
<link rel="stylesheet" href="/web/BlackFruit-web/css/index.css">
|
||||
<link rel="stylesheet" href="/web/BlackFruit-web/css/viewer.min.css">
|
||||
|
||||
<!-- 首页脚本 -->
|
||||
<script src="/web/BlackFruit-web/js/index.js"></script>
|
||||
<!-- 3D 地球依赖:Three.js(请在 /web/BlackFruit-web/vender/three/three.min.js 放置库文件) -->
|
||||
<script src="/web/BlackFruit-web/vender/three/three.min.js"></script>
|
||||
<!-- 3D 地球效果脚本 -->
|
||||
<script src="/web/BlackFruit-web/js/globe.js"></script>
|
||||
<script src="/web/BlackFruit-web/js/viewer.min.js"></script>
|
||||
|
||||
</head>
|
||||
@@ -32,6 +37,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 3D 地球容器(Three.js 渲染输出挂载到这里) -->
|
||||
<div id="bannerGlobe" class="banner-globe"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="banner-s">
|
||||
|
||||
184
js/globe.js
Normal file
184
js/globe.js
Normal file
@@ -0,0 +1,184 @@
|
||||
// 3D 地球效果(首页 banner 右侧)
|
||||
// 依赖:Three.js(全局 THREE 对象),脚本路径建议:/web/BlackFruit-web/vender/three/three.min.js
|
||||
|
||||
(function () {
|
||||
function initGlobe() {
|
||||
var container = document.getElementById("bannerGlobe");
|
||||
if (!container) return;
|
||||
if (typeof THREE === "undefined") {
|
||||
// Three.js 未加载时直接跳过,不影响其它功能
|
||||
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();
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user