zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    生成函数解决多重集合的计数问题
    kmp板子
    poj1001
    【题解】洛谷P1315 [NOIP2011TG] 观光公交(前缀和+贪心)
    【题解】洛谷P1941 [NOIP2014TG] 飞扬的小鸟(背包DP)
    【题解】洛谷P2679 [NOIP2015TG] 子串(DP+滚动数组)
    【题解】洛谷P1514 [NOIP2010TG] 引水入城(DFS+DP)
    【题解】洛谷P1052 [NOIP2005TG] 过河(DP+离散化)
    [arc063F]Snuke's Coloring 2-[线段树+观察]
    [agc001E]BBQ Hard[组合数性质+dp]
  • 原文地址:https://www.cnblogs.com/gaozhiqiang/p/11462234.html
Copyright © 2011-2022 走看看