Threejs3D地球標記中國地圖位置
先看效果
地球預覽視頻效果
用到的庫
TweenJS
(動畫庫)用來做相機轉場的動畫Jquery
(這里只用到一個 each 循環方法,可以使用 js 去寫)ThreeJS
(3D 地球制作)100000.json
(全國城市經緯度)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 ,此項目僅用于交流學習