ThreeJS——在3D地球上標記中國地圖板塊

Threejs3D地球標記中國地圖位置

先看效果

地球預覽視頻效果

用到的庫

  1. TweenJS (動畫庫)用來做相機轉場的動畫
  2. Jquery(這里只用到一個 each 循環方法,可以使用 js 去寫)
  3. ThreeJS (3D 地球制作)
  4. 100000.json(全國城市經緯度)
  5. d3.v6.js用來設置平面轉3D效果(本來考慮做成3D的中國地圖板塊,最后因效果看起來比較美觀還是考慮用線條嵌入球體的方式去實現,這里有小伙伴考慮制作3D的地圖板塊可以下載這個庫)

適用范圍

用于獲取地圖的位置以及到下一個目的地的總路程,可以將實際路程轉成自己配置的路程,以及正在路上的標識,可以用頭像表示,經過的地方可以嵌入鏈接點擊進行跳轉

設置基礎場景

<div id="map"><canvas id="c3d" class="c2d"></canvas>
</div>
<div id="demo"></div>
const Dom = document.querySelector("#c3d");
const width = Dom.clientWidth;
const height = Dom.clientHeight;

如果是 Vue 寫的話需要從onMounted生命周期中獲取 Dom 元素

// 紋理加載器
const loader = new THREE.TextureLoader();
// 渲染器
let renderer;
// 相機
let camera;
// 場景
let scene;
// 燈光
let light;
// 相機控制
let controls;
// 動畫
let tween;
// 其他
let earthMesh,stars,radius,labelRenderer,label,labels,labelsable,labelimg;
/*** 初始化渲染器* */
function initRenderer() {// antialias: true, alpha: true 抗鋸齒設置renderer = new THREE.WebGLRenderer({canvas: Dom,antialias: true,alpha: true,});// window.devicePixelRatio 設備像素比renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(width, height);labelRenderer = new CSS2DRenderer();labelRenderer.domElement.style.position = "absolute";labelRenderer.domElement.style.top = "0px";labelRenderer.domElement.style.pointerEvents = "none";labelRenderer.setSize(width, height);document.getElementById("map").appendChild(labelRenderer.domElement);
}/*** 初始化相機*/
function initCamera() {camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);camera.position.set(0, 0, 10);camera.lookAt(0, 0, 0);window.camera = camera;
}
/*** 初始化場景*/
function initScene() {scene = new THREE.Scene();scene.background = new THREE.Color(0x1c3262);// 霧// scene.fog = new THREE.Fog(0x020924, 200, 1000)window.scene = scene;
}/*** 初始化 相機控制*/
function initControls() {controls = new OrbitControls(camera, renderer.domElement);// 阻尼慣性controls.enableDamping = true;controls.dampingFactor = 0.1;controls.enableZoom = true;controls.autoRotate = false;controls.rotateSpeed = 0.1;controls.autoRotateSpeed = 1;controls.enablePan = true;controls.addEventListener("change", function () {//相機位置與目標觀察點距離const dis = controls.getDistance();console.log(camera.position);});
}/*** 初始化光*/
function initLight() {// 環境光const ambientLight = new THREE.AmbientLight(0xcccccc, 0.2);scene.add(ambientLight);// 平行光let directionalLight = new THREE.DirectionalLight(0xffffff, 0.2);directionalLight.position.set(1, 0.1, 0).normalize();// 平行光2let directionalLight2 = new THREE.DirectionalLight(0xff2ffff, 0.2);directionalLight2.position.set(1, 0.1, 0.1).normalize();scene.add(directionalLight);scene.add(directionalLight2);// 半球光let hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1);hemiLight.position.set(0, 1, 0);scene.add(hemiLight);// 平行光3let directionalLight3 = new THREE.DirectionalLight(0xffffff);directionalLight3.position.set(1, -200, -20);let directionalLight4 = new THREE.DirectionalLight(0xffffff);directionalLight4.position.set(0, 500, 500);// 開啟陰影directionalLight3.castShadow = true;// 設置光邊界directionalLight3.shadow.camera.top = 18;directionalLight3.shadow.camera.bottom = -10;directionalLight3.shadow.camera.left = -52;directionalLight3.shadow.camera.right = 12;scene.add(directionalLight3);
}
// 旋轉隊列
const rotateSlowArr = [];
// 放大并透明 隊列
const bigByOpacityArr = [];
// 移動 隊列
const moveArr = [];// 邊界 繪制點集合
const lines = [];
// 炫光粒子 幾何體
const geometryLz = new THREE.BufferGeometry();
// 炫光粒子 透明度
let opacitys = [];
/*** 渲染函數* */
function renders(time) {time *= 0.0;// 3D對象 旋轉// _y 初始坐標 _s 旋轉速度rotateSlowArr.forEach((obj) => {obj.rotation.y = obj._y + time * obj._s;});bigByOpacityArr.forEach(function (mesh) {//  目標 圓環放大 并 透明mesh._s += 0.01;mesh.scale.set(1 * mesh._s, 1 * mesh._s, 1 * mesh._s);if (mesh._s <= 2) {mesh.material.opacity = 2 - mesh._s;} else {mesh._s = 1;}});moveArr.forEach(function (mesh) {mesh._s += 0.01;let tankPosition = new THREE.Vector3();tankPosition = mesh.curve.getPointAt(mesh._s % 1);mesh.position.set(tankPosition.x, tankPosition.y, tankPosition.z);});if (geometryLz.attributes.position) {geometryLz.currentPos += geometryLz.pointSpeed;for (let i = 0; i < geometryLz.pointSpeed; i++) {opacitys[(geometryLz.currentPos - i) % lines.length] = 0;}for (let i = 0; i < 200; i++) {opacitys[(geometryLz.currentPos + i) % lines.length] =i / 50 > 2 ? 2 : i / 50;}geometryLz.attributes.aOpacity.needsUpdate = true;}renderer.clear();labelRenderer.render(scene, camera);renderer.render(scene, camera);// requestAnimationFrame(renders)// earthMesh.rotation.y += 0.01stars.rotation.y += 0.001;
}

動畫渲染函數

用于制作開始場景鏡頭動畫(由遠到近并附帶略微旋轉)

let p1 = { x: 100, y: 200, z: 200 };
function initTWEEN() {let tweena = cameraCon({ x: 100, y: 200, z: 200 }, 1000);let tweenb = cameraCon({ x: 0, y: 0, z: 10 }, 4000);tweena.chain(tweenb);// tweenb.chain(tweenc);tweenb.onComplete(function () {console.log("結束");drawChart();//相機位置與觀察目標點最小值controls.minDistance = 7;//相機位置與觀察目標點最大值controls.maxDistance = 50;// 上下旋轉范圍/* 		controls.minPolarAngle = -Math.PI /6;controls.maxPolarAngle = Math.PI /4; */// 左右旋轉范圍controls.minAzimuthAngle = -Math.PI / 6;controls.maxAzimuthAngle = Math.PI / 6;});tweena.start();
}
function cameraCon(p2 = { x: p1.x, y: p1.y, z: p1.z }, time = 5000) {var tween1 = new TWEEN.Tween(p1).to(p2, time).easing(TWEEN.Easing.Sinusoidal.InOut);var update = function () {camera.position.set(p1.x, p1.y, p1.z);};tween1.onUpdate(update);return tween1;
}
function cameraCon2(p2 = { x: camera.position.x, y: camera.position.y, z: camera.position.z },time = 2000
) {var tween1 = new TWEEN.Tween(camera.position).to(p2, time).easing(TWEEN.Easing.Sinusoidal.Out);var update = function () {camera.position.set(camera.position.x,camera.position.y,camera.position.z);};tween1.onUpdate(update);return tween1;
}
function animate() {window.requestAnimationFrame((time) => {if (controls) controls.update();TWEEN.update();renders(time);animate();});
}

cameraCon2這個動畫在后面會用到,是根據滾動下方內容進行左右鏡頭旋轉的動畫效果

星空背景

/*** 創建 方形紋理* */
function generateSprite() {const canvas = document.createElement("canvas");canvas.width = 16;canvas.height = 16;const context = canvas.getContext("2d");// 創建顏色漸變const gradient = context.createRadialGradient(canvas.width / 2,canvas.height / 2,0,canvas.width / 2,canvas.height / 2,canvas.width / 2);gradient.addColorStop(0, "rgba(255,255,255,1)");gradient.addColorStop(0.2, "rgba(0,255,255,1)");gradient.addColorStop(0.4, "rgba(0,0,64,1)");gradient.addColorStop(1, "rgba(0,0,0,1)");// 繪制方形context.fillStyle = gradient;context.fillRect(0, 0, canvas.width, canvas.height);// 轉為紋理const texture = new THREE.Texture(canvas);texture.needsUpdate = true;return texture;
}/*** 背景繪制* */
function bg() {const positions = [];const colors = [];// 創建 幾何體const geometry = new THREE.BufferGeometry();for (let i = 0; i < 10000; i++) {let vertex = new THREE.Vector3();vertex.x = Math.random() * 2 - 1;vertex.y = Math.random() * 2 - 1;vertex.z = Math.random() * 2 - 1;positions.push(vertex.x, vertex.y, vertex.z);}// 對幾何體 設置 坐標 和 顏色geometry.setAttribute("position",new THREE.Float32BufferAttribute(positions, 3));// 默認球體geometry.computeBoundingSphere();// ------------- 1 ----------// 星星資源圖片// ParticleBasicMaterial 點基礎材質var starsMaterial = new THREE.ParticleBasicMaterial({map: generateSprite(),size: 2,transparent: true,opacity: 1,//true:且該幾何體的colors屬性有值,則該粒子會舍棄第一個屬性--color,而應用該幾何體的colors屬性的顏色// vertexColors: true,blending: THREE.AdditiveBlending,sizeAttenuation: true,});// 粒子系統 網格stars = new THREE.ParticleSystem(geometry, starsMaterial);stars.scale.set(300, 300, 300);scene.add(stars);
}

此時星空就已經搭建好了,可以左右旋轉試試效果

搭建 3D 地球

其實也就是創建一個球,然后貼個準確的貼圖放在圓上調整一些光感即可

// 地球,月亮 3D層
const landOrbitObject = new THREE.Object3D();
// 地球3D層
const earthObject = new THREE.Object3D();
// 月亮3D層
const moonObject = new THREE.Object3D();
// 地球半徑
const globeRadius = 5;
/*** 球相關加載* */
function earth() {radius = globeRadius;const widthSegments = 100;const heightSegments = 100;const sphereGeometry = new THREE.SphereGeometry(radius,widthSegments,heightSegments);function shine() {var texture = loader.load("./images/blue.png");var spriteMaterial = new THREE.SpriteMaterial({map: texture,transparent: true,opacity: 0.5,depthWrite: false,});var sprite = new THREE.Sprite(spriteMaterial);sprite.scale.set(radius * 3, radius * 3, 1);sprite.rotation.set(-Math.PI / 2, 0, 0);scene.add(sprite);}shine();// 地球const earthTexture = loader.load("./images/微信圖片_20230711093004 (1).jpg");const earthMaterial = new THREE.MeshStandardMaterial({map: earthTexture,});earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);// 月球const moonTexture = loader.load("./images/yueqiu.jpg");const moonMaterial = new THREE.MeshPhongMaterial({ map: moonTexture });const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);moonMesh.scale.set(0.1, 0.1, 0.1);moonMesh.position.x = 10;moonObject.add(moonMesh);// 加入動畫隊列moonObject._y = 0;moonObject._s = 1;rotateSlowArr.push(moonObject);// 地球加入 地球3D層earthObject.add(earthMesh);earthObject.rotation.set(0.5, 2.9, 0.1);earthObject._y = 2.0;earthObject._s = 0.1;// 加入動畫隊列// rotateSlowArr.push(earthObject)// 加入 地球3D層landOrbitObject.add(earthObject);// 加入 月亮3D層landOrbitObject.add(moonObject);scene.add(landOrbitObject);
}/*** 經維度 轉換坐標* THREE.Spherical 球類坐標* lng:經度* lat:維度* radius:地球半徑*/
function lglt2xyz(lng, lat, radius) {// 以z軸正方向為起點的水平方向弧度值const theta = (90 + lng) * (Math.PI / 180);// 以y軸正方向為起點的垂直方向弧度值const phi = (90 - lat) * (Math.PI / 180);return new THREE.Vector3().setFromSpherical(new THREE.Spherical(radius, phi, theta));
}

繪制紅色圓點和點與點之間的飛線效果

/*** 繪制 目標點* */
function spotCircle(spot) {// 圓const geometry1 = new THREE.CircleGeometry(0.02, 100);const material1 = new THREE.MeshBasicMaterial({color: 0xff0000,side: THREE.DoubleSide,});const circle = new THREE.Mesh(geometry1, material1);circle.position.set(spot[0], spot[1], spot[2]);// mesh在球面上的法線方向(球心和球面坐標構成的方向向量)var coordVec3 = new THREE.Vector3(spot[0], spot[1], spot[2]).normalize();// mesh默認在XOY平面上,法線方向沿著z軸new THREE.Vector3(0, 0, 1)var meshNormal = new THREE.Vector3(0, 0, 1);// 四元數屬性.quaternion表示mesh的角度狀態//.setFromUnitVectors();計算兩個向量之間構成的四元數值circle.quaternion.setFromUnitVectors(meshNormal, coordVec3);earthObject.add(circle);// 圓環const geometry2 = new THREE.RingGeometry(0.03, 0.04, 100);// transparent 設置 true 開啟透明const material2 = new THREE.MeshBasicMaterial({color: 0xff0000,side: THREE.DoubleSide,transparent: true,});const circleY = new THREE.Mesh(geometry2, material2);circleY.position.set(spot[0], spot[1], spot[2]);// 指向圓心circleY.lookAt(new THREE.Vector3(0, 0, 0));earthObject.add(circleY);label.position.set(spot[0] - 0.15, spot[1] + 0.04, spot[2]);// 加入動畫隊列bigByOpacityArr.push(circleY);
}
/*** 繪制 兩個目標點并連線* */
function lineConnect(posStart, posEnd) {const v0 = lglt2xyz(posStart[0], posStart[1], globeRadius);const v3 = lglt2xyz(posEnd[0], posEnd[1], globeRadius);// angleTo() 計算向量的夾角const angle = v0.angleTo(v3);let vtop = v0.clone().add(v3);// multiplyScalar 將該向量與所傳入的 標量進行相乘vtop = vtop.normalize().multiplyScalar(globeRadius);let n;if (angle <= 1) {n = (globeRadius / 3) * angle;} else if (angle > 1 && angle < 2) {n = (globeRadius / 3) * Math.pow(angle, 2);} else {n = (globeRadius / 3) * Math.pow(angle, 1.5);}const v1 = v0.clone().add(vtop).normalize().multiplyScalar(globeRadius + n);const v2 = v3.clone().add(vtop).normalize().multiplyScalar(globeRadius + n);// 三維三次貝塞爾曲線(v0起點,v1第一個控制點,v2第二個控制點,v3終點)const curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3);// 繪制 目標位置spotCircle([v0.x, v0.y, v0.z]);spotCircle([v3.x, v3.y, v3.z]);//   線上移動物體moveSpot(curve);const lineGeometry = new THREE.BufferGeometry();// 獲取曲線 上的50個點var points = curve.getPoints(50);var positions = [];var colors = [];var color = new THREE.Color();// 給每個頂點設置演示 實現漸變for (var j = 0; j < points.length; j++) {if (j < 25 || j == 25) {color.set(0xffffff); // 粉色} else if (j < 50 && j > 25) {color.set(0xfffdaa); // 粉色}colors.push(color.r, color.g, color.b);positions.push(points[j].x, points[j].y, points[j].z);}// 放入頂點 和 設置頂點顏色lineGeometry.addAttribute("position",new THREE.BufferAttribute(new Float32Array(positions), 3, true));lineGeometry.addAttribute("color",new THREE.BufferAttribute(new Float32Array(colors), 3, true));const material = new THREE.LineBasicMaterial({vertexColors: true,side: THREE.DoubleSide,});const line = new THREE.Line(lineGeometry, material);earthObject.add(line);
}
/*** 線上移動物體* */
function moveSpot(curve) {// 線上的移動物體const aGeo = new THREE.SphereGeometry(0.04, 0.04, 0.04);const aMater = new THREE.MeshPhongMaterial({color: 0xff0000,side: THREE.DoubleSide,});const aMesh = new THREE.Mesh(aGeo, aMater);// 保存曲線實例aMesh.curve = curve;aMesh._s = 0;moveArr.push(aMesh);earthObject.add(aMesh);
}

用畫布渲染城市信息以及每個點到點的路程的總距離

/*** 畫圖* */function drawChart() {const loader = new THREE.FileLoader();let centers;loader.load("./js/100000_full.json", (data) => {// 點與點let objName = [{ name: "黑龍江省", url: "https://www.baidu.com" },{ name: "內蒙古自治區" },{ name: "四川省" },{ name: "" },];// 線與線let city = [{ to: "黑龍江省" }, { to: "內蒙古自治區" }, { to: "四川省" }];// 當前所在地let location = [{ type: 0 }];const jsondata = JSON.parse(data);let transformedData = [];// 循環$.each(jsondata.features, function (index, item) {const { centroid, center, name } = item.properties;/*    const point = centroid || center || [0, 0];const depth = Math.random() * 0.3 + 0.3; */let proName = item.properties.name;let proName1 = item.properties.name;centers = item.properties.center;objName.forEach((v) => {if (v.name == proName) {labels = createLabel(name, v.url);earthObject.add(labels);if (centers != undefined) {let markPos = lglt2xyz(centers[0], centers[1], 5);spotCircle([markPos.x, markPos.y, markPos.z]);}// lineConnect(item.properties.center,[150,100,100])}});let lastIndex = city.length - 2;city.map((v, index) => {if (v.to == proName1) {v.tojwd = item.properties.center;let indexNum = index + 1;if (indexNum < city.length) {let formCity = city[index];let toCity = city[index + 1];setTimeout(() => {let combinedCity = {to: formCity.to,tojwd: v.tojwd,form: toCity.to,formjwd: toCity.tojwd,};if (combinedCity.formjwd != []) {let distance = getDistance(combinedCity.tojwd[0],combinedCity.tojwd[1],combinedCity.formjwd[0],combinedCity.formjwd[1]);combinedCity.dist = distance;transformedData.push(combinedCity);const coordinates = kilometersToCoordinates(distance,combinedCity.tojwd[1],combinedCity.tojwd[0]);console.log("從" +combinedCity.to +"到" +combinedCity.form +"的距離為" +distance +"公里"); // 輸出兩個坐標之間的距離,單位為公里// 示例用法if (index == lastIndex) {sessionStorage.setItem("last", JSON.stringify(combinedCity));let par = JSON.parse(sessionStorage.getItem("last"));} else {lineConnect(combinedCity.tojwd, combinedCity.formjwd);}// lineConnect(transformedData[0].tojwd, transformedData[0].formjwd)}}, 100);}}});location.forEach((i) => {if (i.type == item.properties.subFeatureIndex) {setTimeout(() => {let last = JSON.parse(sessionStorage.getItem("last"));console.log(last.dist);// 更換自定義路線(公里)last.dist = 300;// 將經緯度轉換為三維坐標const point1 = last.tojwd;const point2 = last.formjwd;// 個人的總步數(公里)let lucheng = 100;let bfb = lucheng / last.dist;let s = point1[0] + (point2[0] - point1[0]) * bfb * 1;let d = point1[1] + (point2[1] - point1[1]) * bfb * 1;const div = document.createElement("div");div.style.color = "#fff";div.style.fontSize = "14px";div.style.textShadow = "1px 1px 2px #047cd6";div.innerHTML = `<img style="width:30px;border-radius:50%" src="./images/head.jpg" />`;labelimg = new CSS2DObject(div);labelimg.scale.set(0.01, 0.01, 0.01);let markPos = lglt2xyz(s, d, 5);labelimg.position.set(markPos.x - 0.1, markPos.y + 0.04, markPos.z);earthObject.add(labelimg);// 定義距離為1000公里setTimeout(() => {lineConnect(point1, [s, d]);}, 101);}, 200);}});});});
}
function getDistance(lat1, lon1, lat2, lon2) {const R = 6371; // 地球半徑,單位為公里const rLat1 = toRadians(lat1);const rLat2 = toRadians(lat2);const deltaLat = toRadians(lat2 - lat1);const deltaLon = toRadians(lon2 - lon1);const a =Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +Math.cos(rLat1) *Math.cos(rLat2) *Math.sin(deltaLon / 2) *Math.sin(deltaLon / 2);const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));const distance = R * c;return distance;
}
function kilometersToCoordinates(distance, latitude, longitude) {const earthRadius = 6371.393; // 地球半徑,單位為公里// 計算緯度差值const latDiff = distance / earthRadius;// 根據緯度計算經度差值const lonDiff =distance / (earthRadius * Math.cos((Math.PI * latitude) / 180));// 計算新的經度和緯度const newLatitude = latitude + latDiff * (180 / Math.PI);const newLongitude = longitude + lonDiff * (180 / Math.PI);// 返回經度和緯度return { latitude: newLatitude, longitude: newLongitude };
}
// 將角度轉換為弧度
function toRadians(degree) {return degree * (Math.PI / 180);
}

創建可點擊跳轉的 url

const createLabel = (name, url) => {const div = document.createElement("div");div.style.color = "#fff";div.style.fontSize = "10px";div.style.textShadow = "1px 1px 2px #047cd6";div.textContent = name;div.style.pointerEvents = "auto";div.style.cursor = "pointer";label = new CSS2DObject(div);div.addEventListener("click", function (event) {if (url != undefined) {window.location.href = url;} else {return;}});label.scale.set(0.01, 0.01, 0.01);return label;
};

創建描邊炫光路徑

const vertexShader = `attribute float aOpacity;uniform float uSize;varying float vOpacity;void main(){gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);gl_PointSize = uSize;vOpacity=aOpacity;}`;
const fragmentShader = `varying float vOpacity;uniform vec3 uColor;float invert(float n){return 1.-n;}void main(){if(vOpacity <=0.2){discard;}vec2 uv=vec2(gl_PointCoord.x,invert(gl_PointCoord.y));vec2 cUv=2.*uv-1.;vec4 color=vec4(1./length(cUv));color*=vOpacity;color.rgb*=uColor;gl_FragColor=color;}`;/*** 邊界炫光路徑* */
function dazzleLight() {const loader = new THREE.FileLoader();loader.load("./js/100000.json", (data) => {const jsondata = JSON.parse(data);console.log(jsondata);// 中國邊界const feature = jsondata.features[0];const province = new THREE.Object3D();province.properties = feature.properties.name;// 點數據const coordinates = feature.geometry.coordinates;coordinates.forEach((coordinate) => {// coordinate 多邊形數據coordinate.forEach((rows) => {// 繪制線// const line = lineDraw(rows, 0xaa381e)const line = lineDraw(rows, 0xaa381e);province.add(line);});});// 添加地圖邊界earthObject.add(province);// 拉平 為一維數組const positions = new Float32Array(lines.flat(1));// 設置頂點geometryLz.setAttribute("position",new THREE.BufferAttribute(positions, 3));// 設置 粒子透明度為 0opacitys = new Float32Array(positions.length).map(() => 0);geometryLz.setAttribute("aOpacity", new THREE.BufferAttribute(opacitys, 1));geometryLz.currentPos = 0;// 炫光移動速度geometryLz.pointSpeed = 10;// 控制 顏色和粒子大小const params = {pointSize: 2.0,pointColor: "#4ec0e9",};// 創建著色器材質const material = new THREE.ShaderMaterial({vertexShader: vertexShader,fragmentShader: fragmentShader,transparent: true, // 設置透明uniforms: {uSize: {value: params.pointSize,},uColor: {value: new THREE.Color(params.pointColor),},},});const points = new THREE.Points(geometryLz, material);earthObject.add(points);});
}/*** 邊框 圖形繪制* @param polygon 多邊形 點數組* @param color 材質顏色* */
let indexBol = true;
function lineDraw(polygon, color) {const lineGeometry = new THREE.BufferGeometry();const pointsArray = new Array();polygon.forEach((row) => {// 轉換坐標const xyz = lglt2xyz(row[0], row[1], globeRadius);// 創建三維點pointsArray.push(xyz);if (indexBol) {// 為了好看 這里只要內陸邊界lines.push([xyz.x, xyz.y, xyz.z]);}});indexBol = false;// 放入多個點lineGeometry.setFromPoints(pointsArray);const lineMaterial = new THREE.LineBasicMaterial({color: color,});return new THREE.Line(lineGeometry, lineMaterial);
}

最后當 ul 中的 li 發生滾動時調用前面所執行的相機轉場動畫的效果

let { scrollY } = window;
let currentSection = 0;
window.addEventListener("scroll", () => {scrollY = window.scrollY;const newSection = Math.round(scrollY / height);console.log(newSection);console.log(camera.position);if (newSection !== currentSection) {currentSection = newSection;console.log(currentSection);if (currentSection == 0) {currentSection = newSection;// console.log('changed', currentSection)let tweena = cameraCon2(camera.position, 1000);console.log(camera.position);let tweenb = cameraCon2({ x: 0, y: 1, z: 10 }, 1000);tweena.chain(tweenb);tweena.start();} else {currentSection = newSection;// console.log('changed', currentSection)let tweena = cameraCon2(camera.position, 1000);console.log(camera.position);let tweenb = cameraCon2({ x: 0, y: -1, z: 10 }, 1000);tweena.chain(tweenb);tweena.start();}}
});

全局初始化

window.onload = () => {// 初始化initTWEEN();initRenderer();initCamera();initScene();initLight();initControls();// 繪制bg();earth();dazzleLight();// 渲染animate();
};

完整代碼地址 earthling ,此項目僅用于交流學習

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/43692.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/43692.shtml
英文地址,請注明出處:http://en.pswp.cn/news/43692.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深入解析IDS/IPS與SSL/TLS和網絡安全

目錄 防火墻 IDS IPS DMZ VPN VPS SSL/TLS 動態IP 靜態IP 防火墻 防火墻是一種網絡安全設備&#xff0c;用于監控和控制網絡流量&#xff0c;保護網絡免受未經授權的訪問、惡意攻擊和威脅。防火墻可以基于規則進行數據包過濾&#xff0c;允許或阻止特定類型的流量通過…

Lead-Lag控制器形式

對于Lead-Lag&#xff08;超前—滯后&#xff09;&#xff0c;有的地方叫做控制器 Controller&#xff0c;有的地方叫補償器 Compensator&#xff0c;有的地方叫濾波器 Filter&#xff0c;都是一個東西。 Lead-Lag也有幾種不同的形式&#xff0c;一種是 G c ( s ) 1 a T s 1…

QT設置widget背景圖片

首先說方法&#xff0c;在給widget或者frame或者其他任何類型的控件添加背景圖時&#xff0c;在樣式表中加入如下代碼&#xff0c;指定某個控件&#xff0c;設置其背景。 類名 # 控件名 { 填充方式&#xff1a;圖片路徑 } 例如&#xff1a; QWidget#Widget {border-image: url…

無涯教程-TensorFlow - 優化器

Optimizers是擴展類&#xff0c;其中包括用于訓練特定模型的附加信息&#xff0c;Optimizers類使用給定的參數初始化&#xff0c;用于提高速度和性能&#xff0c;以訓練特定模型。 TensorFlow的基本Optimizers是- tf.train.Optimizer 此類在tensorflow/python/training/opti…

C語言:深度學習知識儲備

目錄 數據類型 每種類型的大小是多少呢&#xff1f; 變量 變量的命名&#xff1a; 變量的分類&#xff1a; 變量的作用域和生命周期 作用域&#xff1a; 生命周期&#xff1a; 常量 字符串轉義字符注釋 字符串&#xff1a; 轉義字符 操作符&#xff1a; 算術操作符…

圖神經網絡 day2 圖的分類

圖神經網絡基礎算法 1 GCN2 GraphSAGE2.1 采樣&#xff1a;采樣固定長度的鄰居2.2 聚合2.3 GraphSAGE_minibatch2.4 GraphSAGE_embedding 3 GAT4. 圖網絡的分類4.1 遞歸圖神經網絡 RGNN4.2 圖卷積神經網絡GCN4.3 圖注意力網絡 GAT4.4 圖自動編碼 GAE4.5 圖時空網絡 GSTN4.6 圖生…

typeScript 接口和類

工具&#xff1a; PlayGround 接口 接口用來定義對象的結構和類型&#xff0c;描述對象應該具有哪些屬性和方法。 它僅用于聲明&#xff0c;而不是實現&#xff1b; 這對于編寫可重用的代碼非常有用。它可用于&#xff1a; 關鍵字是interface&#xff0c; 注意&#xff1a;它…

OSPF在廣播類型的網絡拓撲中DR和BDR的選舉

指定路由器&#xff08;DR&#xff09;&#xff1a; 一個網段上的其他路由器都和指定路由器&#xff08;DR&#xff09;構成鄰接關系&#xff0c;而不是它們互相之間構成鄰接關系。 備份指定路由器&#xff08;BDR&#xff09;&#xff1a; 當DR出現問題&#xff0c;由BDR接…

redis事務對比Lua腳本區別是什么

redis官方對于lua腳本的解釋&#xff1a;Redis使用同一個Lua解釋器來執行所有命令&#xff0c;同時&#xff0c;Redis保證以一種原子性的方式來執行腳本&#xff1a;當lua腳本在執行的時候&#xff0c;不會有其他腳本和命令同時執行&#xff0c;這種語義類似于 MULTI/EXEC。從別…

中間件: Kafka安裝部署

單機部署 下載二進制包 cd /opt/soft/archive wget http://archive.apache.org/dist/kafka/3.2.0/kafka_2.12-3.2.0.tgz tar -zxf kafka_2.12-3.2.0.tgz -C ../ cd ../kafka_2.12-3.2.0修改配置 vim config/server.propertiesadvertised.listenersPLAINTEXT://39.105.11.50:…

C++系列-函數重載

C系列-函數重載 函數重載函數重載的條件函數重載注意事項引用作為重載函數重載遇到默認參數 函數重載 函數名可以相同&#xff0c; 提高復用性 函數重載的條件 同一個作用域下函數名相同函數參數不同 – 參數個數不同 – 參數順序不同 – 參數類型不同不可以使用返回值作為重…

UI和API自動化測試的失敗原因

一、UI自動化失敗原因&#xff1a; 界面發生了變化&#xff0c;但是腳本沒有更新腳本中的等待時間太短了&#xff0c;導致元素還沒出來就被判定為失敗了網絡因素&#xff0c;網絡如果太慢的話&#xff0c;界面元素的顯示就會滯后執行的時候突然彈出一個窗口影響了元素的定位Ag…

Python web實戰之Django 的跨站點請求偽造(CSRF)保護詳解

關鍵詞&#xff1a;Python、Web、Django、跨站請求偽造、CSRF 大家好&#xff0c;今天我將分享web關于安全的話題&#xff1a;Django 的跨站點請求偽造&#xff08;CSRF&#xff09;保護&#xff0c;介紹 CSRF 的概念、原理和保護方法. 1. CSRF 是什么&#xff1f; CSRF&#…

微服務與Nacos概述-6

RBAC 模型 RBAC 基于角色的訪問控制是實施面向企業安全策略的一種有效的訪問控制方式。 基本思想是&#xff0c;對系統操作的各種權限不是直接授予具體的用戶&#xff0c;而是在用戶集合與權限集合之間建立一個角色集合。每一種角色對應一組相應的權限。一旦用戶被分配了適當…

【MySQL】MySQL基礎知識詳解(一)

MySQL select列的別名去除重復行空值參與運算著重號查詢常數顯示表結構過濾數據 排序使用 ORDER BY 對查詢到的數據進行排序操作。使用列的別名&#xff0c;進行排序二級排序 分頁LIMIT 分頁顯示公式&#xff1a;&#xff08;當前頁數減一&#xff09;*每頁條數&#xff0c;每頁…

【AIGC】 快速體驗Stable Diffusion

快速體驗Stable Diffusion 引言一、安裝二、簡單使用2.1 一句話文生圖2.2 詳細文生圖 三、進階使用 引言 stable Diffusion是一款高性能的AI繪畫生成工具&#xff0c;相比之前的AI繪畫工具&#xff0c;它生成的圖像質量更高、運行速度更快&#xff0c;是AI圖像生成領域的里程碑…

【100天精通python】Day41:python網絡爬蟲開發_爬蟲基礎入門

目錄 專欄導讀 1網絡爬蟲概述 1.1 工作原理 1.2 應用場景 1.3 爬蟲策略 1.4 爬蟲的挑戰 2 網絡爬蟲開發 2.1 通用的網絡爬蟲基本流程 2.2 網絡爬蟲的常用技術 2.3 網絡爬蟲常用的第三方庫 3 簡單爬蟲示例 專欄導讀 專欄訂閱地址&#xff1a;https://blog.csdn.net/…

【玩轉Linux操作】crond的基本操作

&#x1f38a;專欄【玩轉Linux操作】 &#x1f354;喜歡的詩句&#xff1a;更喜岷山千里雪 三軍過后盡開顏。 &#x1f386;音樂分享【Counting Stars 】 歡迎并且感謝大家指出小吉的問題&#x1f970; 文章目錄 &#x1f354;概述&#x1f354;命令?常用選項 &#x1f354;練…

軟件-常用軟件系統架構

目錄 1.客戶端-服務器架構 2.分布式架構 3.微服務架構 4.事件驅動架構 5.單體架構 6.混合架構 當我們談論系統架構時&#xff0c;可以將其比喻為一座房子的設計和結構&#xff0c;想象一下你計劃建造一座豪華別墅&#xff0c;你需要考慮各種因素&#xff1a;如房子的大小…

linux下常見編譯問題

linux下常見編譯問題 linux-cmake靜態編譯查看系統支持的的libc版本查看程序需要的動態鏈接庫查看程序需要的libc版本freebsd下 linux-cmake靜態編譯 CMakeLists.txt 添加 set(CMAKE_EXE_LINKER_FLAGS "-static")ELF 64-bit LSB executable, AMD x86-64, version 1…