You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
448 lines
12 KiB
448 lines
12 KiB
//#region assets/images/home/earth_light_gray_white.png
|
|
var earth_light_gray_white_default = "" + new URL("../images/earth_light_gray_white-D7Fpf71y.png", import.meta.url).href;
|
|
//#endregion
|
|
//#region assets/images/about/location.svg
|
|
var location_default = "data:image/svg+xml,%3csvg%20viewBox='0%200%2030%2041.1846'%20xmlns='http://www.w3.org/2000/svg'%20xmlns:xlink='http://www.w3.org/1999/xlink'%20width='30.000000'%20height='41.184570'%20fill='none'%20customFrame='%23000000'%3e%3cpath%20id='减去顶层'%20d='M14.898%2041.1845C14.898%2041.1845%2030%2031.7422%2030%2014.8666C30%206.6297%2023.2653%200%2015.102%200C6.73469%200%200%206.6297%200%2014.8666C0%2030.5368%2014.898%2041.1845%2014.898%2041.1845ZM20.2041%2015.0225C20.2041%2017.7964%2017.9198%2020.045%2015.102%2020.045C12.2843%2020.045%2010%2017.7964%2010%2015.0225C10%2012.2487%2012.2843%2010%2015.102%2010C17.9198%2010%2020.2041%2012.2487%2020.2041%2015.0225Z'%20fill='rgb(239,67,53)'%20fill-rule='evenodd'%20/%3e%3c/svg%3e";
|
|
//#endregion
|
|
//#region js/earth.js
|
|
(function() {
|
|
const getUiLang = window.getUiLang || (() => (location.pathname.split("/")[1] === "en" ? "en" : "zh"));
|
|
function markerName(data) {
|
|
return getUiLang() === "en" ? (data.nameEn || data.nameZh) : (data.nameZh || data.nameEn);
|
|
}
|
|
const canvas = document.getElementById("earth-canvas");
|
|
if (!canvas || !window.THREE) return;
|
|
const container = canvas.parentElement;
|
|
const scene = new THREE.Scene();
|
|
const camera = new THREE.PerspectiveCamera(40, container.clientWidth / container.clientHeight, .1, 1e3);
|
|
camera.position.z = 1.5;
|
|
const renderer = new THREE.WebGLRenderer({
|
|
canvas,
|
|
alpha: true,
|
|
antialias: true
|
|
});
|
|
renderer.setSize(container.clientWidth, container.clientHeight);
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
const controls = new THREE.OrbitControls(camera, renderer.domElement);
|
|
controls.enableDamping = true;
|
|
controls.dampingFactor = .05;
|
|
controls.enablePan = true;
|
|
controls.enableZoom = false;
|
|
controls.autoRotate = false;
|
|
controls.rotateSpeed = .1;
|
|
controls.minPolarAngle = Math.PI / 2;
|
|
controls.maxPolarAngle = Math.PI / 2;
|
|
const geometry = new THREE.SphereGeometry(.5, 64, 64);
|
|
const hemiLight = new THREE.HemisphereLight("#e0e0e0", "#6a8aaa", .5);
|
|
scene.add(hemiLight);
|
|
const bounceLight = new THREE.DirectionalLight("#000000", .1);
|
|
bounceLight.position.set(0, -5, 0);
|
|
scene.add(bounceLight);
|
|
const ambientLight = new THREE.AmbientLight("#e0e0e0", .9);
|
|
scene.add(ambientLight);
|
|
const textureLoader = new THREE.TextureLoader();
|
|
const material = new THREE.MeshPhongMaterial({
|
|
color: "#efefef",
|
|
emissive: "#000000",
|
|
shininess: 20,
|
|
transparent: true,
|
|
opacity: .9
|
|
});
|
|
const earth = new THREE.Mesh(geometry, material);
|
|
earth.renderOrder = 0;
|
|
textureLoader.load(earth_light_gray_white_default, function(texture) {
|
|
material.map = texture;
|
|
material.color.setHex(16777215);
|
|
material.needsUpdate = true;
|
|
}, void 0, function(err) {
|
|
console.warn("Earth texture failed to load, using default color.", err);
|
|
});
|
|
scene.add(earth);
|
|
const markersData = [
|
|
{
|
|
nameZh: "上海",
|
|
nameEn: "Shanghai",
|
|
lat: 31.2304,
|
|
lon: 121.4737
|
|
},
|
|
{
|
|
nameZh: "东京",
|
|
nameEn: "Tokyo",
|
|
lat: 35.6762,
|
|
lon: 139.6503
|
|
},
|
|
{
|
|
nameZh: "伊斯坦布尔",
|
|
nameEn: "Istanbul",
|
|
lat: 41.0082,
|
|
lon: 28.9784
|
|
},
|
|
{
|
|
nameZh: "伦敦",
|
|
nameEn: "London",
|
|
lat: 51.5074,
|
|
lon: -.1278
|
|
},
|
|
{
|
|
nameZh: "加尔各答",
|
|
nameEn: "Kolkata",
|
|
lat: 22.5726,
|
|
lon: 88.3639
|
|
},
|
|
{
|
|
nameZh: "北京",
|
|
nameEn: "Beijing",
|
|
lat: 39.9042,
|
|
lon: 116.4074
|
|
},
|
|
{
|
|
nameZh: "华沙",
|
|
nameEn: "Warsaw",
|
|
lat: 52.2297,
|
|
lon: 21.0122
|
|
},
|
|
{
|
|
nameZh: "南京",
|
|
nameEn: "Nanjing",
|
|
lat: 32.0603,
|
|
lon: 118.7969
|
|
},
|
|
{
|
|
nameZh: "吉隆坡",
|
|
nameEn: "Kuala Lumpur",
|
|
lat: 3.139,
|
|
lon: 101.6869
|
|
},
|
|
{
|
|
nameZh: "圣保罗",
|
|
nameEn: "São Paulo",
|
|
lat: -23.5505,
|
|
lon: -46.6333
|
|
},
|
|
{
|
|
nameZh: "圣安东尼奥",
|
|
nameEn: "San Antonio",
|
|
lat: 29.4241,
|
|
lon: -98.4936
|
|
},
|
|
{
|
|
nameZh: "墨西哥城",
|
|
nameEn: "Mexico City",
|
|
lat: 19.4326,
|
|
lon: -99.1332
|
|
},
|
|
{
|
|
nameZh: "多伦多",
|
|
nameEn: "Toronto",
|
|
lat: 43.6532,
|
|
lon: -79.3832
|
|
},
|
|
{
|
|
nameZh: "奥克兰",
|
|
nameEn: "Auckland",
|
|
lat: -36.8485,
|
|
lon: 174.7633
|
|
},
|
|
{
|
|
nameZh: "孟买",
|
|
nameEn: "Mumbai",
|
|
lat: 19.076,
|
|
lon: 72.8777
|
|
},
|
|
{
|
|
nameZh: "安地比斯",
|
|
nameEn: "Antibes",
|
|
lat: 43.5804,
|
|
lon: 7.1251
|
|
},
|
|
{
|
|
nameZh: "山景城",
|
|
nameEn: "Mountain View",
|
|
lat: 37.3861,
|
|
lon: -122.0839
|
|
},
|
|
{
|
|
nameZh: "巴黎",
|
|
nameEn: "Paris",
|
|
lat: 48.8566,
|
|
lon: 2.3522
|
|
},
|
|
{
|
|
nameZh: "布宜诺斯艾利斯",
|
|
nameEn: "Buenos Aires",
|
|
lat: -34.6037,
|
|
lon: -58.3816
|
|
},
|
|
{
|
|
nameZh: "开普敦",
|
|
nameEn: "Cape Town",
|
|
lat: -33.9249,
|
|
lon: 18.4241
|
|
},
|
|
{
|
|
nameZh: "悉尼",
|
|
nameEn: "Sydney",
|
|
lat: -33.8688,
|
|
lon: 151.2093
|
|
},
|
|
{
|
|
nameZh: "新加坡",
|
|
nameEn: "Singapore",
|
|
lat: 1.3521,
|
|
lon: 103.8198
|
|
},
|
|
{
|
|
nameZh: "新德里",
|
|
nameEn: "New Delhi",
|
|
lat: 28.6139,
|
|
lon: 77.209
|
|
},
|
|
{
|
|
nameZh: "曼谷",
|
|
nameEn: "Bangkok",
|
|
lat: 13.7563,
|
|
lon: 100.5018
|
|
},
|
|
{
|
|
nameZh: "法兰克福",
|
|
nameEn: "Frankfurt",
|
|
lat: 50.1109,
|
|
lon: 8.6821
|
|
},
|
|
{
|
|
nameZh: "波哥大",
|
|
nameEn: "Bogotá",
|
|
lat: 4.711,
|
|
lon: -74.0721
|
|
},
|
|
{
|
|
nameZh: "深圳",
|
|
nameEn: "Shenzhen",
|
|
lat: 22.5431,
|
|
lon: 114.0579
|
|
},
|
|
{
|
|
nameZh: "牛津",
|
|
nameEn: "Oxford",
|
|
lat: 51.752,
|
|
lon: -1.2577
|
|
},
|
|
{
|
|
nameZh: "特拉维夫",
|
|
nameEn: "Tel Aviv",
|
|
lat: 32.0853,
|
|
lon: 34.7818
|
|
},
|
|
{
|
|
nameZh: "科伦坡",
|
|
nameEn: "Colombo",
|
|
lat: 6.9271,
|
|
lon: 79.8612
|
|
},
|
|
{
|
|
nameZh: "米兰",
|
|
nameEn: "Milan",
|
|
lat: 45.4642,
|
|
lon: 9.19
|
|
},
|
|
{
|
|
nameZh: "纽约",
|
|
nameEn: "New York",
|
|
lat: 40.7128,
|
|
lon: -74.006
|
|
},
|
|
{
|
|
nameZh: "莫斯科",
|
|
nameEn: "Moscow",
|
|
lat: 55.7558,
|
|
lon: 37.6173
|
|
},
|
|
{
|
|
nameZh: "达卡",
|
|
nameEn: "Dhaka",
|
|
lat: 23.8103,
|
|
lon: 90.4125
|
|
},
|
|
{
|
|
nameZh: "迪拜",
|
|
nameEn: "Dubai",
|
|
lat: 25.2048,
|
|
lon: 55.2708
|
|
},
|
|
{
|
|
nameZh: "邦加罗尔",
|
|
nameEn: "Bengaluru",
|
|
lat: 12.9716,
|
|
lon: 77.5946
|
|
},
|
|
{
|
|
nameZh: "金奈",
|
|
nameEn: "Chennai",
|
|
lat: 13.0827,
|
|
lon: 80.2707
|
|
},
|
|
{
|
|
nameZh: "雅加达",
|
|
nameEn: "Jakarta",
|
|
lat: -6.2088,
|
|
lon: 106.8456
|
|
},
|
|
{
|
|
nameZh: "首尔",
|
|
nameEn: "Seoul",
|
|
lat: 37.5665,
|
|
lon: 126.978
|
|
},
|
|
{
|
|
nameZh: "香港",
|
|
nameEn: "Hong Kong",
|
|
lat: 22.3193,
|
|
lon: 114.1694
|
|
},
|
|
{
|
|
nameZh: "全球总部福州",
|
|
nameEn: "Global HQ Fuzhou",
|
|
lat: 26.0745,
|
|
lon: 119.2965
|
|
}
|
|
];
|
|
const markerMeshes = [];
|
|
let fuzhouMarkerMesh = null;
|
|
function latLonToVector3(lat, lon, radius = 1) {
|
|
const phi = (90 - lat) * (Math.PI / 180);
|
|
const theta = (lon + 180) * (Math.PI / 180);
|
|
const x = -radius * Math.sin(phi) * Math.cos(theta);
|
|
const y = radius * Math.cos(phi);
|
|
const z = radius * Math.sin(phi) * Math.sin(theta);
|
|
return new THREE.Vector3(x, y, z);
|
|
}
|
|
const defaultMarkerGeometry = new THREE.CircleGeometry(.012, 24);
|
|
const defaultMarkerMaterial = new THREE.MeshBasicMaterial({
|
|
color: 15680309,
|
|
side: THREE.FrontSide,
|
|
depthWrite: false,
|
|
transparent: true,
|
|
opacity: 1
|
|
});
|
|
const svgWidth = .03;
|
|
const svgHeight = .04;
|
|
const locationTexture = textureLoader.load(location_default);
|
|
const fuzhouMarkerGeometry = new THREE.PlaneGeometry(svgWidth, svgHeight);
|
|
fuzhouMarkerGeometry.translate(0, svgHeight / 2, 0);
|
|
const fuzhouMarkerMaterial = new THREE.MeshBasicMaterial({
|
|
map: locationTexture,
|
|
color: 16777215,
|
|
side: THREE.FrontSide,
|
|
depthWrite: false,
|
|
transparent: true,
|
|
opacity: 1
|
|
});
|
|
markersData.forEach((data) => {
|
|
const position = latLonToVector3(data.lat, data.lon, .5);
|
|
let marker;
|
|
if (data.nameZh === "全球总部福州") {
|
|
marker = new THREE.Mesh(fuzhouMarkerGeometry, fuzhouMarkerMaterial);
|
|
fuzhouMarkerMesh = marker;
|
|
} else marker = new THREE.Mesh(defaultMarkerGeometry, defaultMarkerMaterial);
|
|
marker.position.copy(position);
|
|
const normal = position.clone().normalize();
|
|
marker.position.add(normal.multiplyScalar(.01));
|
|
marker.renderOrder = 1;
|
|
marker.userData = { name: markerName(data), nameZh: data.nameZh };
|
|
earth.add(marker);
|
|
markerMeshes.push(marker);
|
|
});
|
|
const targetLon = 9.6869;
|
|
earth.rotation.y = -(targetLon * Math.PI / 180) + Math.PI;
|
|
const tooltip = document.createElement("div");
|
|
tooltip.style.position = "absolute";
|
|
tooltip.style.padding = "8px";
|
|
tooltip.style.color = "#2E353A";
|
|
tooltip.style.fontSize = "16px";
|
|
tooltip.style.fontFamily = "MiSans VF";
|
|
tooltip.style.textAlign = "center";
|
|
tooltip.style.lineHeight = "20px";
|
|
tooltip.style.whiteSpace = "nowrap";
|
|
tooltip.style.pointerEvents = "none";
|
|
tooltip.style.display = "none";
|
|
tooltip.style.zIndex = "1000";
|
|
tooltip.style.transform = "translate(-50%, 15px)";
|
|
tooltip.classList.add("tooltip-arrow");
|
|
container.appendChild(tooltip);
|
|
const fuzhouTooltip = tooltip.cloneNode(true);
|
|
fuzhouTooltip.innerHTML = (window.t || ((k) => k))("earth.hq_fuzhou_html");
|
|
container.appendChild(fuzhouTooltip);
|
|
const raycaster = new THREE.Raycaster();
|
|
const mouse = new THREE.Vector2();
|
|
canvas.addEventListener("mousemove", (e) => {
|
|
const rect = canvas.getBoundingClientRect();
|
|
mouse.x = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
raycaster.setFromCamera(mouse, camera);
|
|
const intersects = raycaster.intersectObjects(markerMeshes);
|
|
if (intersects.length > 0) {
|
|
const object = intersects[0].object;
|
|
const name = object.userData.name;
|
|
if (object.userData.nameZh !== "全球总部福州") {
|
|
tooltip.textContent = name;
|
|
tooltip.style.display = "block";
|
|
const vector = object.position.clone();
|
|
object.updateWorldMatrix(true, false);
|
|
vector.setFromMatrixPosition(object.matrixWorld);
|
|
vector.project(camera);
|
|
const containerRect = container.getBoundingClientRect();
|
|
const x = (vector.x * .5 + .5) * rect.width + (rect.left - containerRect.left);
|
|
const y = (-(vector.y * .5) + .5) * rect.height + (rect.top - containerRect.top);
|
|
tooltip.style.left = `${x}px`;
|
|
tooltip.style.top = `${y - 10}px`;
|
|
} else tooltip.style.display = "none";
|
|
document.body.style.cursor = "pointer";
|
|
} else {
|
|
tooltip.style.display = "none";
|
|
document.body.style.cursor = "default";
|
|
}
|
|
});
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
earth.rotation.y += 5e-4;
|
|
markerMeshes.forEach((marker) => {
|
|
marker.lookAt(camera.position);
|
|
const worldPosition = new THREE.Vector3();
|
|
marker.getWorldPosition(worldPosition);
|
|
const viewDirection = new THREE.Vector3().subVectors(camera.position, worldPosition).normalize();
|
|
const dot = worldPosition.clone().normalize().dot(viewDirection);
|
|
let scaleValue = Math.max(0, dot);
|
|
scaleValue = Math.pow(scaleValue, .5);
|
|
marker.scale.setScalar(scaleValue);
|
|
});
|
|
if (fuzhouMarkerMesh) {
|
|
const worldPosition = new THREE.Vector3();
|
|
fuzhouMarkerMesh.getWorldPosition(worldPosition);
|
|
const viewDirection = new THREE.Vector3().subVectors(camera.position, worldPosition).normalize();
|
|
if (worldPosition.clone().normalize().dot(viewDirection) > 0) {
|
|
fuzhouTooltip.style.display = "block";
|
|
const vector = fuzhouMarkerMesh.position.clone();
|
|
fuzhouMarkerMesh.updateWorldMatrix(true, false);
|
|
vector.setFromMatrixPosition(fuzhouMarkerMesh.matrixWorld);
|
|
vector.project(camera);
|
|
const rect = canvas.getBoundingClientRect();
|
|
const containerRect = container.getBoundingClientRect();
|
|
const x = (vector.x * .5 + .5) * rect.width + (rect.left - containerRect.left);
|
|
const y = (-(vector.y * .5) + .5) * rect.height + (rect.top - containerRect.top);
|
|
fuzhouTooltip.style.left = `${x}px`;
|
|
fuzhouTooltip.style.top = `${y - 10}px`;
|
|
} else fuzhouTooltip.style.display = "none";
|
|
}
|
|
controls.update();
|
|
renderer.render(scene, camera);
|
|
}
|
|
animate();
|
|
window.addEventListener("resize", () => {
|
|
const width = container.clientWidth;
|
|
const height = container.clientHeight;
|
|
camera.aspect = width / height;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(width, height);
|
|
});
|
|
})();
|
|
//#endregion
|