实现three.js球体转动的特效,点击国家列表,球体转到相应国家,并在地图标记地标,有点击、划过等特性,需要实现如图效果:https://chn.sectsco.org/,
目前尝试了一下,完全搞不来,存在的问题
1.点击左侧国家列表,右侧球体滚动至相应位置,并且目标位置需在球面中心位置
2.实现与目标网站特效一样的地标位置与特效。。
<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Threejs实现绘制地球,地理位置标注-经纬度转换世界坐标</title>
<script type="text/javascript" src="./threeEarth/steak.js"></script>
<script type="text/javascript" src="./threeEarth/three.js"></script>
<script type="text/javascript" src="./threeEarth/OrbitControls.js"></script>
<script type="text/javascript" src="./threeEarth/SVGLoader.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/20.0.0/tween.umd.js"></script>
<input type="hidden" id="_w_simile" data-inspect-config="3"><script type="text/javascript" src="chrome-extension://odphnbhiddhdpoccbialllejaajemdio/scripts/inspector.js"></script></head>
<body>
<div id="container">
<div id="left"></div>
<div id="right">
<div id="dom" style="width:675px;height:675px"></div>
</div>
</div>
<style>
body {
margin: 0;
overflow: hidden;
}
#container {
display: flex;
height: 100vh;
}
#left {
flex: 1;
overflow-y: auto;
padding: 20px;
}
#right {
flex: 2;
position: relative;
}
</style>
<script type="text/javascript">
var camera;
var renderer;
var radius = 100; // 地球半径
var areas = [{
name: "中国",
position: [104.19, 35.86]
}, {
name: "印度共和国",
position: [78.96, 20.59]
}, {
name: "哈萨克斯坦共和国",
position: [48.01, 66.92]
}, {
name: "吉尔吉斯共和国",
position: [74.76, 41.20]
}, {
name: "巴基斯坦伊斯兰共和国",
position: [69.34, 30.37]
}, {
name: "俄罗斯联邦",
position: [105.31, 61.52]
}, {
name: "塔吉克斯坦共和国",
position: [71.27, 38.86]
}, {
name: "科威特",
position: [47.97, 29.37]
}, {
name: "阿拉伯联合酋长国",
position: [53.84, 23.42]
}, {
name: "缅甸联邦共和国",
position: [95.95, 21.91]
}, {
name: "马尔代夫共和国",
position: [73.22, 3.20]
}, {
name: "巴林王国",
position: [50.63, 25.93]
}, {
name: "卡塔尔国",
position: [51.18, 25.35]
}, {
name: "沙特阿拉伯王国",
position: [45.07, 23.88]
}, {
name: "阿拉伯埃及共和国",
position: [30.80, 26.82]
}, {
name: "斯里兰卡民主社会主义共和国",
position: [7.87, 80.77]
}, {
name: "土耳其共和国",
position: [38.96, 35.24]
}, {
name: "尼泊尔联邦民主共和国",
position: [28.39, 84.12]
}, {
name: "柬埔寨王国",
position: [12.56, 104.99]
}, {
name: "亚美尼亚共和国",
position: [40.06, 45.03]
}, {
name: "阿塞拜疆共和国",
position: [40.14, 47.57]
}, {
name: "蒙古",
position: [46.86, 103.84]
}, {
name: "阿富汗伊斯兰共和国",
position: [33.93, 67.71]
}, {
name: "白俄罗斯共和国",
position: [53.70, 27.95]
}, {
name: "伊朗伊斯兰共和国",
position: [32.42, 53.68]
}, {
name: "乌兹别克斯坦共和国",
position: [41.37, 64.58]
}];
function init() {
// 创建一个场景,它将包含我们所有的元素,如物体,相机和灯光。
var scene = new THREE.Scene();
var cubeLoader = new THREE.CubeTextureLoader();
var textureLoader = new THREE.TextureLoader();
// 创建一个摄像机,它定义了我们正在看的地方
camera = new THREE.PerspectiveCamera(50, 675 / 675, 10, 10000);
// 摄像机位置
camera.position.z = 300;
scene.lookAt(scene.position);
var orbit = new THREE.OrbitControls(camera);
// to disable zoom
//orbit.enableZoom = false;
orbit.enableRotate =true; //启用旋转
orbit.enablePan = true; //启用平移
// 注册一个回调函数,当鼠标拖动球体时触发
orbit.addEventListener('change', function () {
var currentPosition = orbit.target;
console.log("当前 camera 的位置:", camera.position);
});
// 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景
renderer = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
var ambientLight = new THREE.AmbientLight("#ffffff", 1);
scene.add(ambientLight);
// 在屏幕上显示坐标轴
var axes = new THREE.AxisHelper(radius);
scene.add(axes);
createEarth();
createAreaPoint();
var rs = coordinateWorldTurnScreen(10, 0, 10);
console.log(rs)
// 将呈现器的输出添加到HTML元素
document.getElementById("dom").appendChild(renderer.domElement);
// 创建国家列表
var countryList = document.getElementById("left");
areas.forEach(function(area) {
var listItem = document.createElement("div");
listItem.textContent = area.name;
listItem.addEventListener("click", function() {
animateToCountry(area.position);
});
countryList.appendChild(listItem);
});
// 启动动画
renderScene();
// 世界坐标转屏幕坐标
function coordinateWorldTurnScreen(x, y, z) {
let world_vector = new THREE.Vector3(x, y, z);
let vector = world_vector.project(camera);
let halfWidth = window.innerWidth / 2,
halfHeight = window.innerHeight / 2;
return {
x: Math.round(vector.x * halfWidth + halfWidth),
y: Math.round(-vector.y * halfHeight + halfHeight)
}
}
// 创建地球 半径100
function createEarth() {
var earthGeo = new THREE.SphereGeometry(radius, 50, 50);
var textureLoader = new THREE.TextureLoader();
textureLoader.crossOrigin = "anonymous"; // 设置跨域属性为 "anonymous"
var earthMater = new THREE.MeshPhongMaterial({
map: textureLoader.load('https://cdndc.img.ria.ru/dc/kay-n/2023/shos-2023/assets/map.png'),
//transparent: true,
//depthWrite: false,
//side: THREE.DoubleSide,
//blending: THREE.AdditiveBlending,
//opacity: 0.8,
//color: 0xffffff
side: THREE.DoubleSide,
opacity: 1,
color: 0xffffff
});
var earthMesh = new THREE.Mesh(earthGeo, earthMater);
earthMesh.name = "earth"
scene.add(earthMesh)
}
function createAreaPoint() {
// 球面
let sphereGeom = new THREE.SphereGeometry(1, 20, 20),
sphereMat = new THREE.MeshBasicMaterial({
color: 0x03d98e,
wireframe: true
})
let sphere = new THREE.Mesh(sphereGeom, sphereMat)
scene.add(sphere)
// 地标
for (let i = 0, length = areas.length; i < length; i++) {
const position = createPosition(this.areas[i].position)
createHexagon(position); // 地标
}
}
// 添加经纬度线
var latLinesMaterial = new THREE.LineBasicMaterial({ color: 0xc8dfff});
var lonLinesMaterial = new THREE.LineBasicMaterial({ color: 0xc8dfff});
// 经度线
for (var i = -90; i <= 90; i += 10) {
var latLineGeometry = new THREE.Geometry();
for (var j = -90; j <= 90; j += 1) {
var vertex = new THREE.Vector3();
vertex.x = Math.sin(j * Math.PI / 90) * Math.cos(i * Math.PI / 90) * radius;
vertex.y = Math.cos(j * Math.PI / 90) * radius;
vertex.z = Math.sin(j * Math.PI / 90) * Math.sin(i * Math.PI / 90) * radius;
latLineGeometry.vertices.push(vertex);
}
var latLine = new THREE.Line(latLineGeometry, latLinesMaterial);
scene.add(latLine);
}
// 纬度线
for (var i = -90; i <= 90; i += 10) {
var lonLineGeometry = new THREE.Geometry();
for (var j = -90; j <= 90; j += 1) {
var vertex = new THREE.Vector3();
vertex.x = Math.sin(i * Math.PI / 90) * Math.cos(j * Math.PI / 90) * radius;
vertex.y = Math.cos(i * Math.PI / 90) * radius;
vertex.z = Math.sin(i * Math.PI / 90) * Math.sin(j * Math.PI / 90) * radius;
lonLineGeometry.vertices.push(vertex);
}
var lonLine = new THREE.Line(lonLineGeometry, lonLinesMaterial);
scene.add(lonLine);
}
// 创建地标标记
function createHexagon(position) {
var hexagon = new THREE.Object3D()
let hexagonLine = new THREE.CircleGeometry(4, 6)
let hexagonPlane = new THREE.CircleGeometry(3, 6)
let vertices = hexagonLine.vertices
vertices.shift() // 第一个节点是中心点
let material = new THREE.MeshBasicMaterial({
color: 0xffff00,
side: THREE.DoubleSide,
opacity: 0.5
})
let circleLine = new THREE.LineLoop(hexagonLine, material)
let circlePlane = new THREE.Mesh(hexagonPlane, material)
circleLine.position.copy(position)
circlePlane.position.copy(position)
circlePlane.lookAt(new THREE.Vector3(0, 0, 0))
circleLine.lookAt(new THREE.Vector3(0, 0, 0))
hexagon.add(circleLine)
hexagon.add(circlePlane)
scene.add(hexagon);
}
function renderScene() {
orbit.update();
TWEEN.update(); // 更新 tween.js 动画状态
// 使用requestAnimationFrame函数进行渲染
requestAnimationFrame(renderScene);
renderer.setClearColor(0xffffff, 1);
renderer.render(scene, camera);
}
function animateToCountry(position) {
var targetPosition = createPosition(position); // 将经纬度转换为三维坐标
var targetHeight = 300; // 相机的目标高度
var duration = 1000; // 动画持续时间,单位毫秒
// 创建 tween.js 动画对象
var tween = new TWEEN.Tween({ x: camera.position.x, y: camera.position.y, z: camera.position.z })
.to({ x: targetPosition.x, y: targetPosition.y, z: targetPosition.z }, duration)
.easing(TWEEN.Easing.Quadratic.InOut) // 使用 Quadratic InOut 缓动函数实现平滑效果
.onUpdate(function(position) {
// 保持相机高度
position.y = targetHeight;
camera.position.set(position.x, position.y, position.z);
camera.lookAt(scene.position); // 保持相机朝向场景中心
})
.onComplete(function () {
console.log('tween--finish');
})
.start(); // 启动动画
}
// 渲染的场景
renderer.setSize(675, 675);
renderer.render(scene, camera);
}
window.onload = init;
// 坐标转换,
function createPosition(lnglat) {
let spherical = new THREE.Spherical
spherical.radius = radius;
const lng = lnglat[0]
const lat = lnglat[1]
const theta = (lng + 90) * (Math.PI / 180)
const phi = (90 - lat) * (Math.PI / 180)
spherical.phi = phi; // phi是方位面(水平面)内的角度,范围0~360度
spherical.theta = theta; // theta是俯仰面(竖直面)内的角度,范围0~90度
let position = new THREE.Vector3()
position.setFromSpherical(spherical)
return position
}
// 随着窗体的变化修改场景
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
//renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 监听窗体调整大小事件
//window.addEventListener('resize', onResize, false);
</script>
</body></html>