概况如下:
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
,设置光源位置为场景中心位置,并将光源加入场景中。
// 初始化场景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);</pre></div>
通过canvas
定义地球材质。
// canvas画地图函数,因为性能问题,线条不再canvas中实现,w表示宽度,h表示高度,worldPos表示世界地图经纬度信息var createCanvas = function (w, h, worldPos) {var canvas = document.createElement('canvas');canvas.width = w;canvas.height = h;var context = canvas.getContext('2d');var centerX = w / 2;var centerY = h / 2;var average = w / 360;// 绘制背景颜色context.fillStyle = earthBallColor;context.fillRect(0, 0, w, h);// canvas中绘制地图方法function canvasLineFun (childrenPosition) {context.fillStyle = earthBallPlaneColor;context.moveTo(centerX + childrenPosition[0][0] * average, centerY - childrenPosition[0][1] * average);childrenPosition.forEach(function (posItem) {context.lineTo(centerX + posItem[0] * average, centerY - posItem[1] * average);})context.closePath();context.fill();}worldPos.forEach(function (item) {canvasLineFun(item);})return canvas;}
定义地球及其材质,地球通过SphereGeometry
来实现,通过THREE.CanvasTexture
来引入canvas
创建的贴图。
// 创建地球earthBall = new THREE.Mesh(new THREE.SphereGeometry(earthBallSize, 50, 50), new THREE.MeshBasicMaterial({map: new THREE.CanvasTexture(createCanvas(2048, 1024, worldGeometry)),side: THREE.FrontSide}));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 drawWorldLine = function (pos, identify) {var posArray = [];pos.forEach(function (item) {var pointPosition = getPosition(item[0] + 90, item[1], earthBallSize);posArray.push(new THREE.Vector3(pointPosition.x, pointPosition.y, pointPosition.z));})// 绘制的线条需要关闭,第二个参数默认为false,表示不关闭var curve = new THREE.CatmullRomCurve3(posArray, true);var points = curve.getPoints(500);var geometry = new THREE.Geometry().setFromPoints(points);// 定义线条var line = new MeshLine();line.setGeometry(geometry);// 定义线条材质var material = new MeshLineMaterial({color: worldLineColor,lineWidth: worldLineWidth})// 绘制地图lineGeometryObj['lineGeometry' + identify] = new THREE.Mesh(line.geometry, material);// 将地图加入场景scene.add(lineGeometryObj['lineGeometry' + identify])}
获取世界地图经纬度信息及计算绘制球体地图参数方法。
// 获取世界经纬度信息函数var getWorldGeometry = function () {$.ajax({type : "GET", //提交方式 url : "./code/world.json",async: false,success : function(response) {//返回数据根据结果进行相应的处理 worldGeometry = [];// 绘制世界地图response.features.forEach(function (worldItem, worldItemIndex) {var length = worldItem.geometry.coordinates.length;var multipleBool = length > 1 ? true : false;worldItem.geometry.coordinates.forEach(function (worldChildItem, worldChildItemIndex) {if (multipleBool) {// 值界可以使用的经纬度信息if (worldChildItem.length && worldChildItem[0].length == 2) {worldGeometry.push(worldChildItem);}// 需要转换才可以使用的经纬度信息if (worldChildItem.length && worldChildItem[0].length > 2) {worldChildItem.forEach(function (countryItem, countryItenIndex) {worldGeometry.push(countryItem);})}} else {var countryPos = null;if (worldChildItem.length > 1) {countryPos = worldChildItem;} else {countryPos = worldChildItem[0];}if (countryPos) {worldGeometry.push(countryPos);}}})})} })}
球体地图线条通过position
值来实现位置的确认,动画使用requestAnimationFrame
来实现。
// 执行函数var render = function () {scene.rotation.y -= 0.01;renderer.render(scene, camera);orbitcontrols.update();requestAnimationFrame(render);}