概況如下:
1、SphereGeometry
實現自轉的地球;
2、THREE.CatmullRomCurve3
實現球體線條地圖點確定;
3、THREE.Math.degToRad
,Math.sin
,Math.cos
實現地圖經緯度與三位坐標x,y,z之間的轉換;
4、MeshLine
用於繪制線條;
5、canvas
用於繪制球體世界地圖貼圖,通過THREE.CanvasTexture
引入。
效果圖如下:
預覽地址:three.js通過canvas實現球體世界平面地圖
初始化場景、相機、渲染器,設置相機位置,初始化光源,光源采用HemisphereLight
,設置光源位置為場景中心位置,並將光源加入場景中。
1 // 初始化場景 2 var scene = new THREE.Scene(); 3 // 初始化相機,第一個參數為攝像機視錐體垂直視野角度,第二個參數為攝像機視錐體長寬比, 4 // 第三個參數為攝像機視錐體近端面,第四個參數為攝像機視錐體遠端面 5 var camera = new THREE.PerspectiveCamera(20, dom.clientWidth / dom.clientHeight, 1, 100000); 6 // 設置相機位置,對應參數分別表示x,y,z位置 7 camera.position.set(0, 0, 200); 8 var renderer = new THREE.WebGLRenderer({ 9 alpha: true, 10 antialias: true 11 }); 12 // 設置光照 13 scene.add(new THREE.HemisphereLight('#ffffff', '#ffffff', 1));
設置場景窗口尺寸,並且初始化控制器,窗口尺寸默認與瀏覽器窗口尺寸保持一致,最后將渲染器加載到dom中。
1 // 設置窗口尺寸,第一個參數為寬度,第二個參數為高度 2 renderer.setSize(dom.clientWidth, dom.clientHeight); 3 // 初始化控制器 4 var orbitcontrols = new THREE.OrbitControls(camera,renderer.domElement); 5 // 將渲染器加載到dom中 6 dom.appendChild(renderer.domElement);
通過canvas
定義地球材質。
1 // canvas畫地圖函數,因為性能問題,線條不再canvas中實現,w表示寬度,h表示高度,worldPos表示世界地圖經緯度信息 2 var createCanvas = function (w, h, worldPos) { 3 var canvas = document.createElement('canvas'); 4 canvas.width = w; 5 canvas.height = h; 6 var context = canvas.getContext('2d'); 7 var centerX = w / 2; 8 var centerY = h / 2; 9 var average = w / 360; 10 // 繪制背景顏色 11 context.fillStyle = earthBallColor; 12 context.fillRect(0, 0, w, h); 13 // canvas中繪制地圖方法 14 function canvasLineFun (childrenPosition) { 15 context.fillStyle = earthBallPlaneColor; 16 context.moveTo(centerX + childrenPosition[0][0] * average, centerY - childrenPosition[0][1] * average); 17 childrenPosition.forEach(function (posItem) { 18 context.lineTo(centerX + posItem[0] * average, centerY - posItem[1] * average); 19 }) 20 context.closePath(); 21 context.fill(); 22 } 23 worldPos.forEach(function (item) { 24 canvasLineFun(item); 25 }) 26 return canvas; 27 }
定義地球及其材質,地球通過SphereGeometry
來實現,通過THREE.CanvasTexture
來引入canvas
創建的貼圖。
1 // 創建地球 2 earthBall = new THREE.Mesh(new THREE.SphereGeometry(earthBallSize, 50, 50), new THREE.MeshBasicMaterial({ 3 map: new THREE.CanvasTexture(createCanvas(2048, 1024, worldGeometry)), 4 side: THREE.FrontSide 5 })); 6 scene.add(earthBall);
標記地點經緯度坐標與三維x,y,z坐標轉換方法。
1 // 經緯度轉換函數,longitude表示經度,latitude表示唯獨,radius表示球體半徑 2 var getPosition = function (longitude, latitude, radius) { 3 // 將經度,緯度轉換為rad坐標 4 var lg = THREE.Math.degToRad(longitude); 5 var lt = THREE.Math.degToRad(latitude); 6 var temp = radius * Math.cos(lt); 7 // 獲取x,y,z坐標 8 var x = temp * Math.sin(lg); 9 var y = radius * Math.sin(lt); 10 var z = temp * Math.cos(lg); 11 return { 12 x: x, 13 y: y, 14 z: z 15 } 16 }
繪制世界地圖線條方法
1 // 繪制世界地圖線條函數 2 var drawWorldLine = function (pos, identify) { 3 var posArray = []; 4 pos.forEach(function (item) { 5 var pointPosition = getPosition(item[0] + 90, item[1], earthBallSize); 6 posArray.push(new THREE.Vector3(pointPosition.x, pointPosition.y, pointPosition.z)); 7 }) 8 // 繪制的線條需要關閉,第二個參數默認為false,表示不關閉 9 var curve = new THREE.CatmullRomCurve3(posArray, true); 10 var points = curve.getPoints(500); 11 var geometry = new THREE.Geometry().setFromPoints(points); 12 // 定義線條 13 var line = new MeshLine(); 14 line.setGeometry(geometry); 15 // 定義線條材質 16 var material = new MeshLineMaterial({ 17 color: worldLineColor, 18 lineWidth: worldLineWidth 19 }) 20 // 繪制地圖 21 lineGeometryObj['lineGeometry' + identify] = new THREE.Mesh(line.geometry, material); 22 // 將地圖加入場景 23 scene.add(lineGeometryObj['lineGeometry' + identify]) 24 }
獲取世界地圖經緯度信息及計算繪制球體地圖參數方法
1 // 獲取世界經緯度信息函數 2 var getWorldGeometry = function () { 3 $.ajax({ 4 type : "GET", //提交方式 5 url : "./code/world.json", 6 async: false, 7 success : function(response) {//返回數據根據結果進行相應的處理 8 worldGeometry = []; 9 // 繪制世界地圖 10 response.features.forEach(function (worldItem, worldItemIndex) { 11 var length = worldItem.geometry.coordinates.length; 12 var multipleBool = length > 1 ? true : false; 13 worldItem.geometry.coordinates.forEach(function (worldChildItem, worldChildItemIndex) { 14 if (multipleBool) { 15 // 值界可以使用的經緯度信息 16 if (worldChildItem.length && worldChildItem[0].length == 2) { 17 worldGeometry.push(worldChildItem); 18 } 19 // 需要轉換才可以使用的經緯度信息 20 if (worldChildItem.length && worldChildItem[0].length > 2) { 21 worldChildItem.forEach(function (countryItem, countryItenIndex) { 22 worldGeometry.push(countryItem); 23 }) 24 } 25 } else { 26 var countryPos = null; 27 if (worldChildItem.length > 1) { 28 countryPos = worldChildItem; 29 } else { 30 countryPos = worldChildItem[0]; 31 } 32 if (countryPos) { 33 worldGeometry.push(countryPos); 34 } 35 } 36 }) 37 }) 38 } 39 }) 40 }
球體地圖線條通過position
值來實現位置的確認,動畫使用requestAnimationFrame
來實現。
1 // 執行函數 2 var render = function () { 3 scene.rotation.y -= 0.01; 4 renderer.render(scene, camera); 5 orbitcontrols.update(); 6 requestAnimationFrame(render); 7 }