概況如下:
1、THREE.CylinderGeometry,THREE.SphereGeometry繪制地圖上的標記;
2、THREE.CanvasTexture用於加載canvas繪制的字體;
3、THREE.Shape,MeshLine用於實現平面地圖;
4、THREE.ExtrudeGeometry用於將繪制的平面地圖沿Z軸拉伸,出現3d效果;
5、THREE.CubicBezierCurve3用於繪制軌跡曲線;
效果圖如下:

預覽地址:three.js實現世界地圖城市遷徙圖
初始化場景、相機、渲染器,設置相機位置。
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, 400); 8 var renderer = new THREE.WebGLRenderer({ 9 alpha: true, 10 antialias: true 11 });
設置場景窗口尺寸,並且初始化控制器,窗口尺寸默認與瀏覽器窗口尺寸保持一致,最后將渲染器加載到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);
繪制平面地圖方法,通過THREE.Shape來實現。
1 // 繪制地圖函數 2 var drawShape = function (pos) { 3 var shape = new THREE.Shape(); 4 // 計算平均每格占比 5 var average = getAverage(); 6 shape.moveTo(pos[0][0], pos[0][1]); 7 pos.forEach(function (item) { 8 shape.lineTo(item[0], item[1]); 9 }) 10 return shape; 11 }
ExturdeGeometry配置參數。
1 // ExturdeGeometry配置參數 2 var options = { 3 depth: zHeight, // 定義圖形拉伸的深度,默認100 4 steps: 0, // 拉伸面方向分為多少級,默認為1 5 bevelEnabled: true, // 表示是否有斜角,默認為true 6 bevelThickness: 0, // 斜角的深度,默認為6 7 bevelSize: 0, // 表示斜角的高度,高度會疊加到正常高度 8 bebelSegments: 0, // 斜角的分段數,分段數越高越平滑,默認為1 9 curveSegments: 0 // 拉伸體沿深度方向分為多少段,默認為1 10 }
將平面地圖拉伸,模擬出現3d效果,通過THREE.ExtrudeGeometry來實現。
1 // 將shape轉換為ExtrudeGeometry 2 var transition3d = function (shapeObj, identify) { 3 var geometry = new THREE.ExtrudeGeometry(shapeObj, options); 4 var material1 = new THREE.MeshBasicMaterial({ 5 color: faceColor 6 }); 7 var material2 = new THREE.MeshBasicMaterial({ 8 color: sideColor 9 }); 10 // 繪制地圖 11 shapeGeometryObj['shapeGeometry' + identify] = new THREE.Mesh(geometry, [material1, material2]); 12 // 將地圖加入場景 13 scene.add(shapeGeometryObj['shapeGeometry' + identify]) 14 }
繪制世界地圖參數方法
1 // 計算繪制地圖參數函數 2 var drawShapeOptionFun = function () { 3 // 繪制世界地圖 4 worldGeometry.features.forEach(function (worldItem, worldItemIndex) { 5 var length = worldItem.geometry.coordinates.length; 6 var multipleBool = length > 1 ? true : false; 7 worldItem.geometry.coordinates.forEach(function (worldChildItem, worldChildItemIndex) { 8 if (multipleBool) { 9 // 值界可以使用的經緯度信息 10 if (worldChildItem.length && worldChildItem[0].length == 2) { 11 transition3d(drawShape(worldChildItem), '' + worldItemIndex + worldChildItemIndex); 12 } 13 // 需要轉換才可以使用的經緯度信息 14 if (worldChildItem.length && worldChildItem[0].length > 2) { 15 worldChildItem.forEach(function (countryItem, countryItenIndex) { 16 transition3d(drawShape(countryItem), '' + worldItemIndex + worldChildItemIndex + countryItenIndex); 17 }) 18 } 19 } else { 20 var countryPos = null; 21 if (worldChildItem.length > 1) { 22 countryPos = worldChildItem; 23 } else { 24 countryPos = worldChildItem[0]; 25 } 26 if (countryPos) { 27 transition3d(drawShape(countryPos), '' + worldItemIndex + worldChildItemIndex); 28 } 29 } 30 }) 31 }) 32 }
通過canvas實現說明文字方法。
1 // canvas實現文字函數 2 var getCanvasFont = function (w, h, textValue, fontColor) { 3 var canvas = document.createElement('canvas'); 4 canvas.width = w; 5 canvas.height = h; 6 var ctx = canvas.getContext('2d'); 7 ctx.fillStyle = textBackground; 8 ctx.fillRect(0, 0, w, h); 9 ctx.font = h + "px '微軟雅黑'"; 10 ctx.textAlign = 'center'; 11 ctx.textBaseline = 'middle'; 12 ctx.fillStyle = fontColor; 13 ctx.fillText(textValue, w / 2, h / 2); 14 $('body').append(canvas) 15 return canvas; 16 }
繪制城市標記方法。
1 /** 繪制標記函數 2 * pos表示經緯度信息 3 * textValue表示標記內容 4 * fontColor表示標記字體顏色 5 * fontSize表示字體大小 6 **/ 7 var drawMarkingFont = function (option, markingIndex) { 8 var average = getAverage(); 9 var cityX = option.pos[0]; 10 var cityY = option.pos[1]; 11 var markingGroup = new THREE.Group(); 12 // 圓錐體 13 var cylinder = new THREE.Mesh( 14 new THREE.CylinderGeometry(circularRadio, 0, circularHeight, 50, 50, false), 15 new THREE.MeshBasicMaterial({ 16 color: markingColor 17 }) 18 ) 19 // 球體 20 var ball = new THREE.Mesh( 21 new THREE.SphereGeometry(circularRadio, 30, 30), 22 new THREE.MeshBasicMaterial({ 23 color: markingColor 24 }) 25 ) 26 ball.position.set(cityX, cityY, circularHeight + zHeight); 27 cylinder.position.set(cityX, cityY, circularHeight / 2 + zHeight); 28 cylinder.rotation.x = 1.5; 29 // 添加文字說明 30 var textLength = option.textValue.split('').length; 31 var texture = new THREE.CanvasTexture(getCanvasFont(textLength * option.fontSize * average, option.fontSize * average, option.textValue, option.fontColor)); 32 var fontMesh = new THREE.Sprite( 33 new THREE.SpriteMaterial({ 34 map: texture 35 }) 36 ) 37 fontMesh.scale.x = option.fontSize / average * textLength; 38 fontMesh.scale.y = option.fontSize / average; 39 // 定義提示文字顯示位置 40 fontMesh.position.set(cityX, cityY, circularHeight + circularRadio / 2 + zHeight / 2 + option.fontSize / average + 0.5); 41 markingGroup.add(ball); 42 markingGroup.add(cylinder); 43 markingGroup.add(fontMesh); 44 markingObj['markingGroup' + markingIndex] = markingGroup; 45 scene.add(markingGroup); 46 }
城市遷徙線條繪制。
1 // 繪制遷徙線條函數 2 var drawMetapLine = function (v0, v3) { 3 var v1 = {}; 4 v1.x = (v0.x + v3.x) / 2; 5 v1.y = (v0.y + v3.y) / 2; 6 v1.z = 6; 7 // 繪制貝塞爾曲線 8 var curve = new THREE.CubicBezierCurve3(v0, v1, v1, v3); 9 var geometry = new THREE.Geometry(); 10 geometry.vertices = curve.getPoints(100); 11 var line = new MeshLine(); 12 line.setGeometry(geometry); 13 var material = new MeshLineMaterial({ 14 color: meshLineColor, 15 lineWidth: lineWidth 16 }) 17 return { 18 curve: curve, 19 lineMesh: new THREE.Mesh(line.geometry, material) 20 } 21 }
繪制遷徙圖方法。
1 // 繪制遷徙圖 2 var drawMetap = function () { 3 var average = getAverage(); 4 var beijing = {x: 116.4551, y: 40.2539, z: zHeight}; 5 var lundun = {x: 0.5, y: 51.3, z: zHeight}; 6 // 經緯度信息 7 var metapArray = []; 8 // 組裝線條連接經緯度信息 9 markingPos.marking.forEach(function (markingItem) { 10 metapArray.push({ 11 x: markingItem.pos[0], 12 y: markingItem.pos[1], 13 z: zHeight 14 }) 15 }) 16 // 線條集合 17 var animateDots = []; 18 // 存放線條對象集合 19 var groupLines = new THREE.Group(); 20 // 繪制遷徙線條 21 metapArray.forEach(function (metapItem, metapIndex) { 22 if (metapIndex > 0) { 23 var line = drawMetapLine(metapArray[0], metapItem); 24 groupLines.add(line.lineMesh); 25 animateDots.push(line.curve.getPoints(metapNum)); 26 } 27 }) 28 // 添加遷徙線條到場景中 29 scene.add(groupLines); 30 // 添加線上滑動的物質 31 var aGroup = new THREE.Group(); 32 for (var i = 0; i < animateDots.length; i ++) { 33 for (var j = 0; j < markingNum; j ++) { 34 var aGeo = new THREE.SphereGeometry(dotWidth, 10, 10); 35 var aMater = new THREE.MeshPhongMaterial({ 36 color: markingColor, 37 transparent: true, 38 opacity: 1 - j * 1 / markingNum 39 }) 40 var aMesh = new THREE.Mesh(aGeo, aMater); 41 aGroup.add(aMesh); 42 } 43 } 44 var vIndex = 0; 45 // 表示第一次循環運行 46 var firstBool = true; 47 function animationLine() { 48 aGroup.children.forEach(function (elem, index) { 49 var _index = parseInt(index / markingNum); 50 // 保證當前數組與遷徙軌跡匹配 51 var index2 = index - _index * markingNum; 52 var _vIndex = 0; 53 if (firstBool) { 54 _vIndex = vIndex - index2 % markingNum >= 0 ? vIndex - index2 % markingNum : 0; 55 } else { 56 _vIndex = vIndex - index2 % markingNum >= 0 ? vIndex - index2 % markingNum : 150 + vIndex - index2; 57 } 58 var v = animateDots[_index][_vIndex]; 59 elem.position.set(v.x, v.y, v.z); 60 }) 61 vIndex ++; 62 if (vIndex > metapNum) { 63 vIndex = 0; 64 } 65 if (vIndex == 150 && firstBool) { 66 firstBool = false; 67 } 68 requestAnimationFrame(animationLine); 69 } 70 scene.add(aGroup); 71 animationLine(); 72 }
世界地圖城市遷徙通過position值來實現位置的確認,動畫使用requestAnimationFrame來實現。
1 // 執行函數 2 var render = function () { 3 scene.rotation.x = -0.8; 4 renderer.render(scene, camera); 5 orbitcontrols.update(); 6 requestAnimationFrame(render); 7 }
