概况如下:
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 }