概況如下:1、SphereGeometry實現自轉的地球;2、THREE.ImageUtils.loadTexture載入地圖貼圖材質;3、THREE.Math.degToRad,Math.sin,Math.cos實現地圖經緯度與三位坐標x,y,z之間的轉換;4、軌跡中根據分段數與相應國家gdp值來 ...
概況如下:
1、SphereGeometry實現自轉的地球;
2、THREE.ImageUtils.loadTexture載入地圖貼圖材質;
3、THREE.Math.degToRad,Math.sin,Math.cos實現地圖經緯度與三位坐標x,y,z之間的轉換;
4、軌跡中根據分段數與相應國家gdp值來實現城市標記。
效果圖如下:
預覽地址:three.js實現球體地球城市模擬遷徙
初始化場景、相機、渲染器,設置相機位置,初始化光源,光源採用HemisphereLight,設置光源位置為場景中心位置,並將光源加入場景中
// 初始化場景 var scene = new THREE.Scene(); // 初始化相機,第一個參數為攝像機視錐體垂直視野角度,第二個參數為攝像機視錐體長寬比, // 第三個參數為攝像機視錐體近端面,第四個參數為攝像機視錐體遠端面 var camera = new THREE.PerspectiveCamera(20, dom.clientWidth / dom.clientHeight, 1, 100000); // 設置相機位置,對應參數分別表示x,y,z位置 camera.position.set(0, 0, 200); var renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); // 設置光照 scene.add(new THREE.HemisphereLight('#ffffff', '#ffffff', 1));
設置場景視窗尺寸,並且初始化控制器,視窗尺寸預設與瀏覽器視窗尺寸保持一致,最後將渲染器載入到dom中。
// 設置視窗尺寸,第一個參數為寬度,第二個參數為高度 renderer.setSize(dom.clientWidth, dom.clientHeight); // 初始化控制器 var orbitcontrols = new THREE.OrbitControls(camera,renderer.domElement); // 將渲染器載入到dom中 dom.appendChild(renderer.domElement);
定義地球及其材質,地球通過SphereGeometry
來實現,通過ImageUtils
來導入貼圖。
// 定義地球材質 var earthTexture = THREE.ImageUtils.loadTexture(earthImg, {}, function () { renderer.render(scene, camera); }); // 創建地球 earthBall = new THREE.Mesh(new THREE.SphereGeometry(earthBallSize, 50, 50), new THREE.MeshBasicMaterial({ map: earthTexture })); scene.add(earthBall);
標記地點經緯度坐標與三維x,y,z坐標轉換方法。
// 經緯度轉換函數,longitude表示經度,latitude表示唯獨,radius表示球體半徑 var getPosition = function (longitude, latitude, radius) { // 將經度,緯度轉換為rad坐標 var lg = THREE.Math.degToRad(longitude); var lt = THREE.Math.degToRad(latitude); var temp = radius * Math.cos(lt); // 獲取x,y,z坐標 var x = temp * Math.sin(lg); var y = radius * Math.sin(lt); var z = temp * Math.cos(lg); return { x: x, y: y, z: z } }
添加兩個城市之間軌跡的方法。
// 添加軌跡函數 var addLine = function (v0, v3) { var angle = (v0.angleTo(v3) * 180) / Math.PI; var aLen = angle * 0.5 * (1 - angle / (Math.PI * earthBallSize * parseInt(earthBallSize / 10))); var hLen = angle * angle * 1.2 * (1 - angle / (Math.PI * earthBallSize * parseInt(earthBallSize / 10))); var p0 = new THREE.Vector3(0, 0, 0); // 法線向量 var rayLine = new THREE.Ray(p0, getVCenter(v0.clone(), v3.clone())); // 頂點坐標 var vtop = rayLine.at(hLen / rayLine.at(1).distanceTo(p0)); // 控制點坐標 var v1 = getLenVcetor(v0.clone(), vtop, aLen); var v2 = getLenVcetor(v3.clone(), vtop, aLen); // 繪製貝塞爾曲線 var curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3); var geometry = new THREE.Geometry(); geometry.vertices = curve.getPoints(100); var line = new MeshLine(); line.setGeometry(geometry); var material = new MeshLineMaterial({ color: metapLineColor, lineWidth: 0.1, transparent: true, opacity: 1 }) return { curve: curve, lineMesh: new THREE.Mesh(line.geometry, material) } }
軌跡上運動的小球實現方法。
var animateDots = []; // 線條對象集合 var groupLines = new THREE.Group(); // 線條 marking.children.forEach(function (item) { var line = addLine(marking.children[0].position, item.position); groupLines.add(line.lineMesh); animateDots.push(line.curve.getPoints(metapNum)); }) scene.add(groupLines); // 線上滑動的小球 var aGroup = new THREE.Group(); for (var i = 0; i < animateDots.length; i ++) { for (var j = 0; j < markingNum; j ++) { var aGeo = new THREE.SphereGeometry(slideBallSize, 10, 10); var aMaterial = new THREE.MeshBasicMaterial({ color: slideBallColor, transparent: true, opacity: 1 - j * 0.02 }) var aMesh = new THREE.Mesh(aGeo, aMaterial); aGroup.add(aMesh); } } var vIndex = 0; var firstBool = true; function animationLine () { aGroup.children.forEach(function (elem, index) { var _index = parseInt(index / markingNum); var index2 = index - markingNum * _index; var _vIndex = 0; if (firstBool) { _vIndex = vIndex - index2 % markingNum >= 0 ? vIndex - index2 % markingNum : 0; } else { _vIndex = vIndex - index2 % markingNum >= 0 ? vIndex - index2 % markingNum : metapNum + vIndex - index2; } var v = animateDots[_index][_vIndex]; elem.position.set(v.x, v.y, v.z); }) vIndex ++; if (vIndex > metapNum) { vIndex = 0; } if (vIndex == metapNum && firstBool) { firstBool = false; } requestAnimationFrame(animationLine); } scene.add(aGroup);
標記地點通過position
值來實現位置的確認,動畫使用requestAnimationFrame
來實現。
// 執行函數 var render = function () { scene.rotation.y -= 0.01; renderer.render(scene, camera); orbitcontrols.update(); requestAnimationFrame(render); }