three.js实现世界地图城市迁徙图
概况如下:
1、THREE.CylinderGeometry,THREE.SphereGeometry绘制地图上的标记;
2、THREE.CanvasTexture用于加载canvas绘制的字体;
3、THREE.Shape,MeshLine用于实现平面地图;
4、THREE.ExtrudeGeometry用于将绘制的平面地图沿Z轴拉伸,出现3d效果;
5、THREE.CubicBezierCurve3用于绘制轨迹曲线;
效果图如下:

预览地址:three.js实现世界地图城市迁徙图
初始化场景、相机、渲染器,设置相机位置。
// 初始化场景
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, 400);
var renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
设置场景窗口尺寸,并且初始化控制器,窗口尺寸默认与浏览器窗口尺寸保持一致,最后将渲染器加载到dom中
// 设置窗口尺寸,第一个参数为宽度,第二个参数为高度
renderer.setSize(dom.clientWidth, dom.clientHeight);
// 初始化控制器
var orbitcontrols = new THREE.OrbitControls(camera,renderer.domElement);
// 将渲染器加载到dom中
dom.appendChild(renderer.domElement);
绘制平面地图方法,通过THREE.Shape来实现。
// 绘制地图函数
var drawShape = function (pos) {
var shape = new THREE.Shape();
// 计算平均每格占比
var average = getAverage();
shape.moveTo(pos[0][0], pos[0][1]);
pos.forEach(function (item) {
shape.lineTo(item[0], item[1]);
})
return shape;
}
ExturdeGeometry配置参数。
// ExturdeGeometry配置参数
var options = {
depth: zHeight, // 定义图形拉伸的深度,默认100
steps: 0, // 拉伸面方向分为多少级,默认为1
bevelEnabled: true, // 表示是否有斜角,默认为true
bevelThickness: 0, // 斜角的深度,默认为6
bevelSize: 0, // 表示斜角的高度,高度会叠加到正常高度
bebelSegments: 0, // 斜角的分段数,分段数越高越平滑,默认为1
curveSegments: 0 // 拉伸体沿深度方向分为多少段,默认为1
}
将平面地图拉伸,模拟出现3d效果,通过THREE.ExtrudeGeometry来实现。
// 将shape转换为ExtrudeGeometry
var transition3d = function (shapeObj, identify) {
var geometry = new THREE.ExtrudeGeometry(shapeObj, options);
var material1 = new THREE.MeshBasicMaterial({
color: faceColor
});
var material2 = new THREE.MeshBasicMaterial({
color: sideColor
});
// 绘制地图
shapeGeometryObj['shapeGeometry' + identify] = new THREE.Mesh(geometry, [material1, material2]);
// 将地图加入场景
scene.add(shapeGeometryObj['shapeGeometry' + identify])
}
绘制世界地图参数方法
// 计算绘制地图参数函数
var drawShapeOptionFun = function () {
// 绘制世界地图
worldGeometry.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) {
transition3d(drawShape(worldChildItem), '' + worldItemIndex + worldChildItemIndex);
}
// 需要转换才可以使用的经纬度信息
if (worldChildItem.length && worldChildItem[0].length > 2) {
worldChildItem.forEach(function (countryItem, countryItenIndex) {
transition3d(drawShape(countryItem), '' + worldItemIndex + worldChildItemIndex + countryItenIndex);
})
}
} else {
var countryPos = null;
if (worldChildItem.length > 1) {
countryPos = worldChildItem;
} else {
countryPos = worldChildItem[0];
}
if (countryPos) {
transition3d(drawShape(countryPos), '' + worldItemIndex + worldChildItemIndex);
}
}
})
})
}
通过canvas实现说明文字方法。
// canvas实现文字函数
var getCanvasFont = function (w, h, textValue, fontColor) {
var canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
ctx.fillStyle = textBackground;
ctx.fillRect(0, 0, w, h);
ctx.font = h + "px '微软雅黑'";
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = fontColor;
ctx.fillText(textValue, w / 2, h / 2);
$('body').append(canvas)
return canvas;
}
绘制城市标记方法。
/** 绘制标记函数
* pos表示经纬度信息
* textValue表示标记内容
* fontColor表示标记字体颜色
* fontSize表示字体大小
**/
var drawMarkingFont = function (option, markingIndex) {
var average = getAverage();
var cityX = option.pos[0];
var cityY = option.pos[1];
var markingGroup = new THREE.Group();
// 圆锥体
var cylinder = new THREE.Mesh(
new THREE.CylinderGeometry(circularRadio, 0, circularHeight, 50, 50, false),
new THREE.MeshBasicMaterial({
color: markingColor
})
)
// 球体
var ball = new THREE.Mesh(
new THREE.SphereGeometry(circularRadio, 30, 30),
new THREE.MeshBasicMaterial({
color: markingColor
})
)
ball.position.set(cityX, cityY, circularHeight + zHeight);
cylinder.position.set(cityX, cityY, circularHeight / 2 + zHeight);
cylinder.rotation.x = 1.5;
// 添加文字说明
var textLength = option.textValue.split('').length;
var texture = new THREE.CanvasTexture(getCanvasFont(textLength * option.fontSize * average, option.fontSize * average, option.textValue, option.fontColor));
var fontMesh = new THREE.Sprite(
new THREE.SpriteMaterial({
map: texture
})
)
fontMesh.scale.x = option.fontSize / average * textLength;
fontMesh.scale.y = option.fontSize / average;
// 定义提示文字显示位置
fontMesh.position.set(cityX, cityY, circularHeight + circularRadio / 2 + zHeight / 2 + option.fontSize / average + 0.5);
markingGroup.add(ball);
markingGroup.add(cylinder);
markingGroup.add(fontMesh);
markingObj['markingGroup' + markingIndex] = markingGroup;
scene.add(markingGroup);
}
城市迁徙线条绘制。
// 绘制迁徙线条函数
var drawMetapLine = function (v0, v3) {
var v1 = {};
v1.x = (v0.x + v3.x) / 2;
v1.y = (v0.y + v3.y) / 2;
v1.z = 6;
// 绘制贝塞尔曲线
var curve = new THREE.CubicBezierCurve3(v0, v1, v1, v3);
var geometry = new THREE.Geometry();
geometry.vertices = curve.getPoints(100);
var line = new MeshLine();
line.setGeometry(geometry);
var material = new MeshLineMaterial({
color: meshLineColor,
lineWidth: lineWidth
})
return {
curve: curve,
lineMesh: new THREE.Mesh(line.geometry, material)
}
}
绘制迁徙图方法。
// 绘制迁徙图
var drawMetap = function () {
var average = getAverage();
var beijing = {x: 116.4551, y: 40.2539, z: zHeight};
var lundun = {x: 0.5, y: 51.3, z: zHeight};
// 经纬度信息
var metapArray = [];
// 组装线条连接经纬度信息
markingPos.marking.forEach(function (markingItem) {
metapArray.push({
x: markingItem.pos[0],
y: markingItem.pos[1],
z: zHeight
})
})
// 线条集合
var animateDots = [];
// 存放线条对象集合
var groupLines = new THREE.Group();
// 绘制迁徙线条
metapArray.forEach(function (metapItem, metapIndex) {
if (metapIndex > 0) {
var line = drawMetapLine(metapArray[0], metapItem);
groupLines.add(line.lineMesh);
animateDots.push(line.curve.getPoints(metapNum));
}
})
// 添加迁徙线条到场景中
scene.add(groupLines);
// 添加线上滑动的物质
var aGroup = new THREE.Group();
for (var i = 0; i < animateDots.length; i ++) {
for (var j = 0; j < markingNum; j ++) {
var aGeo = new THREE.SphereGeometry(dotWidth, 10, 10);
var aMater = new THREE.MeshPhongMaterial({
color: markingColor,
transparent: true,
opacity: 1 - j * 1 / markingNum
})
var aMesh = new THREE.Mesh(aGeo, aMater);
aGroup.add(aMesh);
}
}
var vIndex = 0;
// 表示第一次循环运行
var firstBool = true;
function animationLine() {
aGroup.children.forEach(function (elem, index) {
var _index = parseInt(index / markingNum);
// 保证当前数组与迁徙轨迹匹配
var index2 = index - _index * markingNum;
var _vIndex = 0;
if (firstBool) {
_vIndex = vIndex - index2 % markingNum >= 0 ? vIndex - index2 % markingNum : 0;
} else {
_vIndex = vIndex - index2 % markingNum >= 0 ? vIndex - index2 % markingNum : 150 + vIndex - index2;
}
var v = animateDots[_index][_vIndex];
elem.position.set(v.x, v.y, v.z);
})
vIndex ++;
if (vIndex > metapNum) {
vIndex = 0;
}
if (vIndex == 150 && firstBool) {
firstBool = false;
}
requestAnimationFrame(animationLine);
}
scene.add(aGroup);
animationLine();
}
世界地图城市迁徙通过position值来实现位置的确认,动画使用requestAnimationFrame来实现。
// 执行函数
var render = function () {
scene.rotation.x = -0.8;
renderer.render(scene, camera);
orbitcontrols.update();
requestAnimationFrame(render);
}
three.js实现世界地图城市迁徙图的更多相关文章
- arcgis api 4.x for js 集成 Echarts4 实现模拟迁徙图效果(附源码下载)
前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 4.x for js:esri 官网 api,里面详细的介绍 arcgis api 4.x 各个类 ...
- vue中使用 echarts3.0 或 echarts2.0 (模拟迁徙图,折线图)
一.echarts3.0(官网: http://echarts.baidu.com/) 首先通过npm安装echarts依赖,安装的为3.0版本 npm install echarts -s 也可以使 ...
- 前端学习-使用JS库Leaflet.js生成世界地图并获取标注地址经纬度。
介绍:Leaflet是一个开源的JavaScript库,对移动端友好且对地图有很好的交互性. 大小仅仅只有 33 KB, 同时具有大多数地图所需要的特点. Leaflet设计的非常简单易懂, 同时具有 ...
- Echarts4+EchartsGL 3D迁徙图(附源码)
最近遇到些Echarts迁徙图问题,在实现二维地图的迁徙图后开始开发3D迁徙图,在网上一查,发现3D版本迁徙图资料较少,自己研究并借鉴一些资料后写了一个小demo,希望能帮大家少走些弯路,共同学习. ...
- 手把手教你DIY一个春运迁徙图(一)
换了新工作,也确定了我未来数据可视化的发展方向.新年第一篇博客,又逢春运,这篇技术文章就来交给大家如何做一个酷炫的迁徙图(支持移动哦).(求star 代码点这里) 迁徙图的制作思路分为静态的元素和变换 ...
- jQuery与原生js实现banner轮播图
jQuery与原生js实现banner轮播图: (jq需自己加载)(图片需自己加载) <!DOCTYPE html> <html> <head> <meta ...
- openlayers4 入门开发系列之迁徙图篇(附源码下载)
前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...
- 原生JS实现简易轮播图
原生JS实现简易轮播图(渐变?) 最近做网页总是会用到轮播图,我就把之前写的轮播图单独拿出来吧,如果有...如果真的有人也需要也可以复制去用用啊..哈~.. window.onload = funct ...
- 基于js全屏动画焦点图幻灯片
今天给大家分享一款基于js全屏动画焦点图幻灯片.这款焦点图内的内容以动画形式出现和消失.效果图如下: 在线预览 源码下载 实现的代码. html代码: <div class="sl ...
随机推荐
- 这几种JavaScript语法不要轻易使用,容易出事
文章目录 12种不宜使用的JavaScript语法 1. == 2. with 3. eval 4. continue 5. switch 贯穿 6. 单行的块结构 7. ++和-- 8. 位运算符 ...
- C# WPF实用的注册窗体
时间如流水,只能流去不流回! 点赞再看,养成习惯,这是您给我创作的动力! 本文 Dotnet9 https://dotnet9.com 已收录,站长乐于分享dotnet相关技术,比如Winform.W ...
- .net(C#数据库访问) Mysql,Sql server,Sqlite,Access四种数据库的连接方式
便签记录Mysql,Sql server,Sqlite,Access四种数据库的简单连接方式 //using MySql.Data.MySqlClient; #region 执行简单SQL语句,使用M ...
- python通过人脸识别全面分析好友,一起看透你的“朋友圈”
微信:一个提供即时通讯服务的应用程序,更是一种生活方式,超过数十亿的使用者,越来越多的人选择使用它来沟通交流. 不知从何时起,我们的生活离不开微信,每天睁开眼的第一件事就是打开微信,关注着朋友圈里好友 ...
- JavaScript 基于HTML5 canvas 获取文本占用的像素宽度
基于HTML5 canvas 获取文本占用的像素宽度 by:授客 QQ:1033553122 直接上代码 // 获取单行文本的像素宽度 getTextPixelWith(text, fontS ...
- PMBOK 指南 第三章 项目经理的角色
项目经理的角色 3.1 概述 项目经理类似于交响乐团的指挥 成员与角色 在团队中的职责 知识和技能:具备项目管理知识.技术知识.理解和经验. 3.2 定义 项目经理是由执行组织委派,领导团队实现项目目 ...
- 平时常用sql
总结一下平时用到最多的sql语句 1.特殊日期 --今天凌晨 ,) --明天凌晨 ,,) --当周周一(每周从周日开始) ,) --当月的第一天 ,) --当月的最后一天 ,,,)) --今年的第一天 ...
- 集合系列 Map(十四):WeakedHashMap
WeakedHashMap 也是 Map 集合的哈希实现,但其余 HashMap 的不同之处在于.其每个节点的 value 引用是弱引用,可以方便 GC 回收. public class WeakHa ...
- 想精通分布式以及高并发架构?那你得先搞定ZooKeeper架构原理!
Zookeeper是分布式一致性问题的工业解决方案,是Apache Hadoop下解决分布式一致性的一个组件,后被分离出来成为Apache的顶级项目. 工程来源:是雅虎公司内部项目,据说雅虎内部很多项 ...
- java map在JSTL EL中的小应用--<c:forEach>遍历Map<>泛型
准 备 数 据 :(自己准备吧少年,考验你时候到了!!) /** 结构示意图: 类型: List集合 map对象 LIst集合 Person类对象 String name : int age mLis ...