想到3D,我最想想到的是WebGL,但是通过对WebGL的学习了解,实现地球效果很难达到。
其次还有echarts GL,做过图表的应该知道echarts,echarts是一个不错的做图表的框架,他安装简单,使用便捷。echarts GL是echarts用来实现三维图标的工具,他是可以实现3D的效果的。
实现步骤如下:
1、从官网上下载echarts.min.js和echarts-gl.min.js文件,引入到html 页面中
//后续会用到,这里先引入
2、为地球创建一个id为earth的div容器
3、下面就是关键的js代码实现功能了,我们先创建一个地球
var dom = document.getElementById("earth")
//关键的三步:创建一个echarts实例、设置属性option、将实例和option联动
var myChart = echarts.init(dom);
option = {
globe:{
show:true,
shading:"color",
//视角控制:
viewControl:{
//projection:"orthographic",
rotateSensitivity:0, //鼠标旋转灵敏度
zoomSensitivity:0,//鼠标缩放灵敏度
autoRotate:true,//地球是否自传
autoRotateAfterStill:0.001,//鼠标停止后多久恢复旋转(为0时暂停后不恢复旋转)
//alpha:160,//视角绕 x 轴,即上下旋转的角度
//beta:-20,//视角绕 y 轴,即左右旋转的角度。
targetCoord: [116.46, 39.92]//定位到哪里
}
},
};
if (option && typeof option === "object") {
myChart.setOption(option, true);
}
4、现在只是创建了一个球,但是没有地图信息,echarts还提供了一些地图信息,有世界地图和中国地球等,我们将这些信息绘制到地球上。现在我们有了球了,还差一个贴图,我们可以自由通过canvas绘制。
// 使用 echarts 绘制世界地图的实例作为纹理
var canvas = document.createElement('canvas');
var mapChart = echarts.init(canvas, null, {
width: 4096, height: 2048
});
mapChart.setOption({
series : [
{
type: 'map',
map: 'world',//world是html页面引入的地图文件名字
// 绘制完整尺寸的 echarts 实例
top: 0, left: 0,
right: 0, bottom: 0,
silent:true,//图形是否不响应和触发鼠标事件,默认为 false,即响应和触发鼠标事件。
boundingCoords: [[-180, 90], [180, -90]],
label:{
textStyle:{
color:"#fff",
fontSize:50
}
},
emphasis:{
itemStyle:{
color:"#2038cc"
}
}
}
]
});
5、把自己写的地图贴图效果放到地球上,完整代码如下
var dom = document.getElementById("earth")
var myChart = echarts.init(dom);
// 使用 echarts 绘制世界地图的实例作为纹理
var canvas = document.createElement('canvas');
var mapChart = echarts.init(canvas, null, {
width: 4096, height: 2048
});
mapChart.setOption({
series : [
{
type: 'map',
map: 'world',
// 绘制完整尺寸的 echarts 实例
top: 0, left: 0,
right: 0, bottom: 0,
silent:true,//图形是否不响应和触发鼠标事件,默认为 false,即响应和触发鼠标事件。
boundingCoords: [[-180, 90], [180, -90]],
label:{
textStyle:{
color:"#fff",
fontSize:50
}
},
emphasis:{
itemStyle:{
color:"#2038cc"
}
}
}
]
});
option = {
globe:{
show:true,
baseTexture: mapChart,
//heightTexture:mapChart,
shading:"color",
//视角控制
viewControl:{
//projection:"orthographic",
/*damping:0,*/
rotateSensitivity:0, //鼠标旋转灵敏度
zoomSensitivity:0,//鼠标缩放灵敏度
autoRotate:true,
autoRotateAfterStill:0.001,//鼠标停止后多久恢复旋转(为0时暂停后不恢复旋转)
//alpha:160,//视角绕 x 轴,即上下旋转的角度
//beta:-20,//视角绕 y 轴,即左右旋转的角度。
targetCoord: [116.46, 39.92]//定位到哪里
}
},
};
if (option && typeof option === "object") {
myChart.setOption(option, true);
}
此时实现的效果如下:
如果你想要修改地球上区域颜色,可以直接在创建地图的series中添加
itemStyle:{
normal:{
areaColor:"rgba(0,0,152,0.5)"
}
}
用echarts GL实现的效果不是很满意,继续尝试,发现了cesium,cesium可以直接调用地图。
1、cesium引入的框架相对比较繁琐,可以下载了cesium后把Build\CesiumUnminified的内容全部拷贝到你的项目里。这个时候你会发现改文件很大,不是很适合,其实里面有很大是用不到的,可以删除。(怎么保留自己有用的呢?当你功能实现后,你可以把cesium.js文件先移出项目,运行后通过控制台看找不到的文件,把这些文件移入项目,多次执行,一直都没有错误为止)。
2、开始实现功能,先在html页面引入cesium.js文件,引入Widgets/widgets.css样式,并创建一个id为cesiumEarth的div容器。
3、下面开始创建地球,cesium的优点是可以引入地图文件,这里引入了天地图
var viewer = new Cesium.Viewer( 'cesiumEarth', {
//需要进行可视化的数据源的集合
animation: false, //是否显示动画控件
homeButton : false,//是否显示Home按钮
fullscreenButton : false,//是否显示全屏按钮
baseLayerPicker: false, //是否显示图层选择控件
geocoder: false, //是否显示地名查找控件
timeline: false, //是否显示时间线控件
sceneModePicker: false, //是否显示投影方式控件
navigationHelpButton: false, //是否显示帮助信息控件
infoBox: false, //是否显示点击要素之后显示的信息
//天地图是官方开元的地图,不需要密钥
imageryProvider: new Cesium.WebMapTileServiceImageryProvider({
url: "http://{s}.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles",
layer: "img",
style: "default",
format: "tiles",
tileMatrixSetID: "w",
credit:new Cesium.Credit('天地图全球影像服务'),
subdomains:['t0',"t1","t2","t3","t4","t5","t6","t7"],
maximumLevel:18,
show: false
})
} );
//(根据太阳/月亮位置启用照明)
/*scene.globe.enableLighting = true;*/
注:地图服务有矢量图和影像服务可以根据你的需求选择,上面引入的是影像服务,下面是矢量图服务
imageryProvider : new Cesium.WebMapTileServiceImageryProvider({
url: "http://t0.tianditu.com/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles",
layer: "tdtVecBasicLayer",
style: "default",
format: "image/jpeg",
tileMatrixSetID: "GoogleMapsCompatible",
show: false
})
4、这个时候你会发现,只有一个地球,没有地名的标注,我们还需要引入中文地名标注。
//全球影像中文注记服务
viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
url: "http://t0.tianditu.com/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default.jpg",
layer: "tdtAnnoLayer",
style: "default",
format: "image/jpeg",
tileMatrixSetID: "GoogleMapsCompatible",
show: false
}));
//全球矢量中文标注服务
viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
url: "http://t0.tianditu.com/cva_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cva&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default.jpg",
layer: "tdtAnnoLayer",
style: "default",
format: "image/jpeg",
tileMatrixSetID: "GoogleMapsCompatible"
}));
5、这个时候看着比较美观了。框架中有很多控件,如果不需要可以通过上面3中设置为false,就不在显示。但是左下角的logo和连接是没办法去掉的,但是可以通过修改css样式进行修改
.cesium-viewer-bottom{
display: none;
}
6、项目本身就有鼠标操作的事件,有时候我们会需要取消这些事件自己定义。
// 禁用默认的事件处理程序
// 如果为真,则允许用户旋转相机。如果为假,相机将锁定到当前标题。此标志仅适用于2D和3D。
scene.screenSpaceCameraController.enableRotate = false;
// 如果为true,则允许用户平移地图。如果为假,相机将保持锁定在当前位置。此标志仅适用于2D和Columbus视图模式。
scene.screenSpaceCameraController.enableTranslate = false;
// 如果为真,允许用户放大和缩小。如果为假,相机将锁定到距离椭圆体的当前距离
scene.screenSpaceCameraController.enableZoom = false;
// 如果为真,则允许用户倾斜相机。如果为假,相机将锁定到当前标题。这个标志只适用于3D和哥伦布视图。
scene.screenSpaceCameraController.enableTilt = false;
// 如果为true,则允许用户使用免费外观。如果错误,摄像机视图方向只能通过转换或旋转进行更改。此标志仅适用于3D和哥伦布视图模式。
scene.screenSpaceCameraController.enableLook = false;
//自定义鼠标事件
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
// 接收用户鼠标(手势)事件
// 处理鼠标按下事件
handler.setInputAction(function(movement) {
// movement: 接收值为一个对象,含有鼠标单击的x,y坐标
// 设置鼠标当前位置
mousePosition = startMousePosition = Cesium.Cartesian3.clone(movement.position);
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
// 处理鼠标移动事件
handler.setInputAction(function(movement) {
// 更新鼠标位置
mousePosition = movement.endPosition;
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 处理鼠标左键弹起事件
handler.setInputAction(function(position) {
//此时鼠标位置position
}, Cesium.ScreenSpaceEventType.LEFT_UP);
7、控制相机运动
camera.flyTo( {
destination : Cesium.Cartesian3.fromDegrees(nowPoint.lng, nowPoint.lat, cameraH ),//使用WGS84
orientation : {
heading : Cesium.Math.toRadians( 0 ),
pitch : Cesium.Math.toRadians( -90 ),
roll : Cesium.Math.toRadians( 0 )
},
duration : 3,//动画持续时间
complete : function()//飞行完毕后执行的动作
{
// addEntities();
canCont=true;
canFly=true;
},
pitchAdjustHeight: -90, // 如果摄像机飞越高于该值,则调整俯仰俯仰的俯仰角度,并将地球保持在视口中。
maximumHeight:5000 // 相机最大飞行高度
} );
8、通过点击获取位置,并将相机移动过去
function flyToPoint(point) {
//将屏幕坐标转换为世界坐标
var pick= new Cesium.Cartesian2(point.x,point.y);
var cartesian = scene.globe.pick(viewer.camera.getPickRay(pick), scene);
//世界坐标转地理坐标(弧度)
var cartographic = scene.globe.ellipsoid.cartesianToCartographic(cartesian);
//地理坐标(弧度)转经纬度坐标
var nowPoint={lng:cartographic.longitude / Math.PI * 180,
lat:cartographic.latitude / Math.PI * 180};
//获取当前相机高度
var cameraH=Math.ceil(viewer.camera.positionCartographic.height);
if(cameraH>1000000){
cameraH=Math.ceil(cameraH/5);
}else if(cameraH>10000){
cameraH=Math.ceil(cameraH/4);
}else if(cameraH>2000){
cameraH=cameraH-2500
}
if(cameraH>=2000 && canFly){
canFly=false;
camera.flyTo( {
destination : Cesium.Cartesian3.fromDegrees(nowPoint.lng, nowPoint.lat, cameraH ),//使用WGS84
orientation : {
heading : Cesium.Math.toRadians( 0 ),
pitch : Cesium.Math.toRadians( -90 ),
roll : Cesium.Math.toRadians( 0 )
},
duration : 3,//动画持续时间
complete : function()//飞行完毕后执行的动作
{
},
pitchAdjustHeight: -90, // 如果摄像机飞越高于该值,则调整俯仰俯仰的俯仰角度,并将地球保持在视口中。
maximumHeight:5000 // 相机最大飞行高度
} );
}
}
创建的3D地球效果图如下: