three.js實現世界地圖城市遷徙圖


概況如下:

1、THREE.CylinderGeometryTHREE.SphereGeometry繪制地圖上的標記;

2、THREE.CanvasTexture用於加載canvas繪制的字體;

3、THREE.ShapeMeshLine用於實現平面地圖;

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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM