一、需求
1. 世界地图效果图
在世界地图上实现飞线效果、IP地址显示
2. 中国地图效果图
在中国地图上实现黄、蓝标牌、 3D 柱状图等,并带有十段线
二、开发说明
echarts v5.0.0+ node v12.0.0+ react class 组件形式
三、开发步骤
引入 echarts
- npm 下载
npm install echarts
- cdn 引入
<script
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"
></script>
开始使用
- 引入地图资源,注册地图(这里以世界地图为例)
import worldJson from "./world.json";
class WorldMap extends Component {
...
componentDidMount() {
this.createMap();
}
createMap = () => {
constructor(props) {
super(props);
this.state = {
options: {},
ipOptions: {},
mapChart: null,
};
}
... //获取配置数据
const dom = document.getElementById(componentConfig.id); // 保证每个地图唯一
var mapChart = echarts.init(dom); // 初始化
echarts.registerMap("world", worldJson); // 注册地图
this.setState({ options, ipOptions }); // 飞线、IP都存state
mapChart.setOption(displayMode === 0 ? options : ipOptions); // 显示方式切换;配置地图选项
this.setState({ mapChart })
};
}
配置地图选项 options
- 地图飞线的配置实现
const options = {
radius: '100%',
tooltip: {
trigger: 'item',
},
legend: {
orient: 'horizontal', //图例的排列方向
// textStyle: { color: '#1a1e45' },
x: 'left', //图例的位置
y: '-20000000000000',
},
visualMap: {
//颜色的设置 dataRange
// textStyle: { color: '#1a1e45' },
x: 'left',
y: 'bottom',
// splitList: [{ start: 0, end: 150000 }],
show: false,
// text:['高','低'],// 文本,默认为数值文本
color: [flyColor], // 组件配置变量
},
geo: {
map: 'world', // 此处需与注册地图命名保持一致
type: 'map',
zoom: 1.2,
label: {
normal: {
show: false,
textStyle: {
color: '#FFFFFF',
},
},
emphasis: {
show: false,
},
},
roam: false, //是否允许缩放
itemStyle: {
normal: {
color: bgColor, //地图背景色,用到组件配置的变量
borderColor: borderColor, //省市边界线
borderWidth: 1,
textStyle: '#fff',
},
emphasis: {
areaColor: selectColor, //悬浮背景
},
},
data: [],
},
series: this.getSeries(centerPoint, mainData, flyLineArr, coordData, flyDirection), // 飞线的配置
}
//飞线配置
getSeries = (centerPoint, mainData, flyLineArr, coordData, flyDirection) => {
let series = [];
let centerPointName = Object.keys(centerPoint)[0] || '北京区域中心';
let centerPointValue = Object.values(centerPoint)[0] || [116, 39];
[[centerPointName, flyLineArr]].forEach((item, i) => {
series.push(
{
type: 'lines',
coordinateSystem: 'geo',
zlevel: 2,
effect: {
show: true,
period: 5, //箭头指向速度,值越小速度越快
trailLength: 0, //特效尾迹长度[0,1]值越大,尾迹越长重
symbol: 'arrow', //箭头图标
symbolSize: 5, //图标大小
color: mainData.iconColor, // 图标颜色
},
lineStyle: {
normal: {
show: true,
width: 1, //尾迹线条宽度
opacity: 1, //尾迹线条透明度
curveness: 0.3, //尾迹线条曲直度
color: mainData.flyColor, // 飞线颜色 - 细线
},
},
data: this.convertData(item[1], coordData, flyDirection, centerPointValue),
},
{
type: 'effectScatter',
radius: '100%',
coordinateSystem: 'geo',
zlevel: 2,
rippleEffect: { //涟漪特效
period: 4, //动画时间,值越小速度越快
brushType: 'stroke', //波纹绘制方式 stroke, fill
scale: 3, //波纹圆环最大限制,值越大波纹越大
color: mainData.rippleColor,
},
label: {
normal: {
show: false,
position: 'right', //显示位置
offset: [5, 0], //偏移设置
formatter: (params) => {
return params.data.name //圆环显示文字
},
fontSize: 13,
},
emphasis: {
show: false,
},
},
symbol: 'circle',
symbolSize: (val) => {
return 5 //圆环大小
},
itemStyle: {
normal: {
show: true,
// areaColor: mainData.pointColor,
// color: mainData.pointColor,
areaColor: "#ade9f4",
color: "#ade9f4",
},
emphasis: {
// areaColor: mainData.pointColor,
areaColor: "#ade9f4",
},
},
data: item[1].map((dataItem) => {
return {
//在这里定义你所要展示的数据
name: dataItem[0].name,
value: coordData[dataItem[0].name]?.concat([dataItem[0].value]),
}
}),
},
//中心点
{
type: 'effectScatter',
radius: '100%',
coordinateSystem: 'geo',
zlevel: 15,
rippleEffect: {
period: 4,
brushType: 'stroke',
scale: 4,
color: '#FFD246',
},
label: {
normal: {
show: false,
position: 'right',
//offset:[5, 0],
color: '#FFD246',
formatter: '{b}',
textStyle: {
color: '#FFD246',
},
},
emphasis: {
show: false,
color: '#FFD246',
},
},
symbol: 'circle',
symbolSize: 5,
itemStyle: {
color: '#FFD246',
},
data: [
{
name: item[0],
value: coordData[item[0]]?.concat([10]),
},
],
}
)
})
return series
};
- 配置 IP 地址显示
const ipOptions = {
tooltip: {
backgroundColor: "rgba(0,0,0,0)",
trigger: "axis",
},
legend: {
show: false,
},
geo: {
map: 'world',
type: 'map',
zoom: 1.2,
label: {
normal: {
show: false,
textStyle: {
color: '#FFFFFF',
},
},
emphasis: {
show: false,
},
},
roam: false, //是否允许缩放
itemStyle: {
normal: {
color: bgColor, //地图背景色
borderColor: borderColor, //省市边界线00fcff 516a89
borderWidth: 1,
textStyle: '#fff',
},
emphasis: {
areaColor: selectColor, //悬浮背景
},
},
data: [],
},
series: [
{
tooltip: {
show: false,
},
type: "effectScatter",
coordinateSystem: "geo",
rippleEffect: {
scale: 10,
brushType: "stroke",
},
showEffectOn: "render",
itemStyle: {
normal: {
color: "#00FFFF", //ip 涟漪颜色
},
},
label: {
normal: {
color: "#fff",
},
},
symbol: "circle",
// symbolSize: [10, 5],
symbolSize: [10, 5],
data: this.convertIPData(ipData, ipCoordData),
zlevel: 1,
},
{
type: "scatter",
coordinateSystem: "geo",
itemStyle: {
color: "#00FFF6", // 光标颜色
},
symbol: img.arrow,
// symbol: 'arrow',
symbolSize: [44, 34],
symbolOffset: [0, -10],
// symbolRotate: 180,
z: 999,
data: this.convertIPData(ipData, ipCoordData),
},
{
type: "scatter",
coordinateSystem: "geo",
label: {
normal: {
show: true,
formatter: function (params) {
let name = params.name;
let value = params.value[2];
let text = `{fline|${value}}`;
return text;
},
color: "#fff",
rich: {
fline: {
padding: [0, 25],
color: "#fff",
fontSize: 14,
fontWeight: 600,
},
tline: {
padding: [0, 27],
color: "#ABF8FF",
fontSize: 12,
},
},
},
emphasis: {
show: true,
},
},
itemStyle: {
color: "#00FFF6",
},
symbol: img.ipbg,
// symbol: "roundRect",
symbolSize: [125, 32],
symbolOffset: [0, -35],
z: 999,
data: this.convertIPData(ipData, ipCoordData),
},
],
};
// IP数据转换
convertIPData = (data, gdGeoCoordMap) => {
// 校验
if (!data) { return }
let res = [];
for (let i = 0; i < data.length; i++) {
let geoCoord = gdGeoCoordMap[data[i].name];
if (geoCoord) {
res.push({
name: data[i].name,
value: geoCoord.concat(data[i].value),
});
}
}
return res;
};
至此,即可完成世界地图的基本配置。
四、开发过程问题及解决方案
1.中国地图和世界地图如何配置?
两者在注册地图时切换地图资源即可展现不同的地图效果。
echarts.registerMap("china", chinaJson);
echarts.registerMap("world", worldJson);
注:中国地图十段线的配置也在地图资源数据内完成。
2.世界地图飞线方向如何控制?
飞线涉及到飞线中心点,飞线方向由coordArr
控制,调换coordArr
内坐标位置即可改变飞线攻击方向。
注:若需要实现多飞线中心,则需修改地图数据结构。
convertData = (data, coordData, flyDirection, centerPointValue) => {
if (!data) { return } //判空
let res = []
for (let i = 0; i < data.length; i++) {
let dataItem = data[i]
let fromCoord = coordData[dataItem[0].name]
let toCoord = centerPointValue //中心点地理坐标
if (fromCoord && toCoord) {
let coordArr = [{
coord: fromCoord, // 飞线去往哪里
value: dataItem[0].value,
}, {
coord: toCoord, // 飞线从哪里出发
},
];
res.push( flyDirection === 0 ? coordArr : coordArr.reverse() ) // 通过 flyDirection 控制飞线方向
}
}
return res
}
3.地图自定义图标如何引入?
echarts 的series
方法中的symbol
属性可以引入图片路径,一共有三种方法:
- 链接引入
在symbol中直接引入图片的路径,注意格式,要加image://
{
type: "scatter",
coordinateSystem: "geo",
label: { ... },
itemStyle: { color: "#00FFF6",},
symbol: "image://http:...",
symbolSize: [600, 240],
symbolOffset: [360, -150],
symbolRotate: 180,
z: 999,
data: this.convertIPData2(dataCenter, ipCoordData),
}
- base64格式引入
base64格式引入,需要注意base64代码串不能换行
{
type: "scatter",
coordinateSystem: "geo",
label: { ... },
itemStyle: { color: "#00FFF6",},
symbol: "image://data:image/png;data:image/png;base64,iVBORw...",
symbolSize: [600, 240],
symbolOffset: [360, -150],
symbolRotate: 180,
z: 999,
data: this.convertIPData2(dataCenter, ipCoordData),
}
- svg图引入
svg图可用notepad++软件或者记事本打开,将属性值复制出来前面加path://即可
{
type: "scatter",
coordinateSystem: "geo",
label: { ... },
itemStyle: { color: "#00FFF6",},
symbol: "path://M512....",
symbolSize: [600, 240],
symbolOffset: [360, -150],
symbolRotate: 180,
z: 999,
data: this.convertIPData2(dataCenter, ipCoordData),
}
4.中国地图边缘高亮实现
echarts提供的series
内itemStyle -normal-boderColor
设置的是各省份之间的边界线颜色,要实现单独的地图边缘高亮,可用geo
属性结合series
生成两个地图叠加,实现想要的效果。
const options = {
...,
geo: {
silent: true,
radius: '100%',
map: "china",
zoom: 1.20,
// zoom: 0.8,
// top: "-6%",
label: {
normal: {
show: false,
textStyle: {
color: "#fff",
},
},
emphasis: {
textStyle: {
color: "#fff",
},
},
},
roam: false,
itemStyle: {
normal: {
areaColor: "rgba(0,255,255,.02)",
borderColor: {
type: 'linear', //设置边缘色渐变
x: 0,
y: 1,
x2: .5,
y2: 0,
colorStops: [
{
offset: 0, color: '#f7e914'
}, {
offset: 0.5, color: '#fbaa0e'
}, {
offset: 1, color: '#306a9f'
}],
global: false
},
borderWidth: 7,
shadowColor: "#35a8ff",
// shadowColor: "#3071a7",
shadowOffsetX: 0,
shadowOffsetY: 38,
shadowBlur: 45,
},
emphasis: {
areaColor: "transparent", //悬浮背景
textStyle: {
color: "#fff",
},
},
},
},
series: [
// 地图
{
map: 'china',
type: 'map',
radius: '100%',
zoom: 1.20,
label: {
normal: {
show: false,
textStyle: {
color: '#FFFFFF',
},
},
emphasis: {
show: false,
},
},
roam: false, //是否允许缩放
itemStyle: {
normal: {
// color: bgColor, //地图背景色
areaColor: bgColor, //地图背景色
borderColor: borderColor, //内边缘颜色
borderWidth: 2,
textStyle: '#fff',
},
emphasis: {
areaColor: selectColor, //悬浮背景
},
},
data: [],
},
... //其他配置
]
}
5. 如何在地图上实现3D柱状图
地图上元素的配置均在options
的series
内完成,柱状图分主干、顶部、底部 三部分绘制实现,代码如下:
series: [
... //其他配置
// 柱状体的主干
{
type: "lines",
zlevel: 5,
effect: {
show: false,
// period: 4, //箭头指向速度,值越小速度越快
// trailLength: 0.02, //特效尾迹长度[0,1]值越大,尾迹越长重
// symbol: 'arrow', //箭头图标
// symbol: imgDatUrl,
symbolSize: 5, // 图标大小
},
lineStyle: {
width: 60, // 尾迹线条宽度
// color: "#f60", //柱状颜色
color: {
type: 'linear', // 线性渐变
x: 0, // x: 从左向右 1 ——> 0
y: 0, // y: 从上向下 1 ——> 0
x2: 0, // x2: 从右向左 1 ——> 0
y2: 1, // y2: 从下向上 1 ——> 0
colorStops: [
{ offset: 0, color: '#ffd43c' },
{ offset: 1, color: '#bf5b2d' }
]
},
opacity: .8, // 尾迹线条透明度
curveness: 0, // 尾迹线条曲直度
},
label: {
show: 0,
position: "end",
formatter: "245",
},
silent: true,
data: this.lineData(dataCenter, ipCoordData),
},
// 柱状体的顶部
{
type: "scatter",
coordinateSystem: "geo",
// geoIndex: 0,
// zlevel: 5,
label: {
normal: {
show: true,
formatter: () => { return ''; },
color: "#fff",
},
emphasis: {
show: true,
},
},
itemStyle: {
color: "#fff", //##柱状顶部颜色
// opacity: .9,
},
symbol: img.orange,
symbolSize: [60, 96],
symbolOffset: [0, -30],
z: 999,
// silent: true,
data: this.scatterData(dataCenter, ipCoordData),
},
// 柱状体的底部
{
type: "scatter",
coordinateSystem: "geo",
geoIndex: 0,
zlevel: 4,
label: {
formatter: "{b}",
position: "bottom",
color: "#fff",
fontSize: 36,
distance: 10,
show: true,
},
symbol: "circle",
symbolSize: [60, 30],
itemStyle: {
color: '#f60',
opacity: .6,
},
silent: true,
data: this.scatterData2(dataCenter, ipCoordData),
},
]
柱状体各部分data
数据转换
// 柱状体的主干
lineData = (dataCenter, ipCoordData) => {
if (!dataCenter) { return }
return dataCenter.map((item) => {
return {
coords: [
ipCoordData[item.name],
[
ipCoordData[item.name],
ipCoordData[item.name] + item.times * this.lineMaxHeight(dataCenter),
],
],
};
});
}
// 柱状体的顶部
scatterData = (dataCenter, ipCoordData) => {
if (!dataCenter) { return }
return dataCenter.map((item) => {
return [
ipCoordData[item.name],
ipCoordData[item.name] + item.times * this.lineMaxHeight(dataCenter),
];
});
}
// 柱状体的底部
scatterData2 = (dataCenter, ipCoordData) => {
if (!dataCenter) { return }
return dataCenter.map((item) => {
return {
name: item.name,
value: ipCoordData[item.name],
};
});
}
6.中国地图黄色标牌如何配置?
自定义标牌的实现也在options
的series
内配置完成。
series:[
..., //其他配置
// 黄色标牌
{
type: "scatter",
coordinateSystem: "geo",
label: {
normal: {
show: true,
formatter: function (params) {
let value = params.value[2];
let times = params.data.times;
let system = params.data.system;
let text = `{fline|数据中心(${value})}\n{tline|攻击总量:${times} 次}\n{tline|攻击系统:${system} 个}`;
return text;
},
color: "#fff",
rich: { // 富文本配置
fline: {
padding: [0, 5], // 调整标牌文字位置
backgroundColor: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [{
offset: 0, color: '#f7c91c' // 0% 处的颜色
}, {
offset: 1, color: '#ffffff00' // 100% 处的颜色
}],
globalCoord: false // 缺省为 false
},
lineHeight: 72,
fontSize: 42,
width: 500,
height: 80,
},
tline: {
padding: [0, 25],
color: "#fff",
fontSize: 36,
lineHeight: 60,
width: 500,
},
},
},
emphasis: {
show: true,
},
},
itemStyle: {
color: "#00FFF6",
},
symbol: img.yellow, //自定义图标
symbolSize: [600, 240],
symbolOffset: [360, -150],
z: 999,
data: this.convertIPData2(dataCenter, ipCoordData),
},
]
五、经验总结
-
关于UI图效果还原,基本都能在options上配置实现,可多测试看效果。
-
echarts 组件案例合集: