前言
前段时间我司领导让做个大屏的展示项目,由于我司的光荣传统,我们并没有设计师,然后领导不知道从哪找的一堆的大屏项目让我参考,于是看着花花绿绿的大屏项目,领导灵光乍泄选中了其中的两个,并对我说,“就用这个,不过你得改改”,“怎么改?”,“我们的主色调用这个,把中间的中国地图换掉换成安徽的,然后做成这样加一个浮动框把这个信息添上去”,并且领导还贴心的给我配了一个说明图。
于是最后,这个大屏的地图展示成了这样:
不过这都是后话了。于是我思考片刻决定,嗯,这个原项目本来不就是echarts做的吗,我也用echarts改吧改吧应该可以吧。OK,说干就干,我兴致冲冲的去下载echarts的官方地图包,然后
啊,这。。不过没有关系,虽然echarts的地图下载链接没有了,但是阿里云还是贴心的提供了矢量图地图下载的配置,我只要弄一个安徽的不就行了,这一步还是很顺利,但是新的问题又出现了,echarts的官网已经找不到任何一个矢量地图的样例了。啊,这。没了参考,我怎么魔改呢,虽然直到项目做完了我才知道还有一个民间维护的makepie里面有样例可以参考,不过这也是后话了。
当时的我百感交集,在调研了很多的项目方案后,最终我决定,听说阿里他们有套自己的可视化库,不行瞅瞅去,于是在看到L7Plot我知道,我可以用它来实现这个方案了。
开干
L7plot是阿里Antv系列里的一个项目,它基于Antv L7的库在这个库的基础上又做了一层封装,这个库在开发地图相关的一些可视化时非常方便。
块状地图展示
入门写的也很清楚安装之后引入就可以了,那么接下来的问题是,我怎样把这个地图变成一个只有轮廓线的地图,并且让地图展示在安徽这个位置上,我看到了官方的一个例子,在我研究了一番之后我明白了操作思路了,可以说l7plot的想法确实挺有意思,在它的map配置项里有一个style属性,当把它改成“blank”之后就可以显示为块状的地图,但是此时你只能看到一张中国地图的轮廓,怎样让窗口显示在安徽这个地方呢?这需要修改多个地方,首先还是要在map属性下,修改center的值,这是一个数组,数组中的两个值分别代表地图加载后显示在中心位置的坐标,这个坐标即位显示中的经纬度,如果你需要让一个省显示在中心,只要找到这个省大概的中心城市替换这个值就好,而刚好合肥的位置在安徽省的中心上,所以将合肥的经纬度替换一下就行。但此时地图加载完只是改变了显示位置,安徽会显得比较小,这时候需要修改minZoom来规定一个地图最小的缩小范围就可以了。
map: {
type: "mapbox",
style: "blank",
center: [117.30794, 31.79322],
minZoom: 3,
maxZoom: 7,
zoom: 1,
pitch: 0,
},
完成了上述操作之后你其实并不能看到任何效果,因为还有较为关键的一步,这个时候看下官方样例你会发现还有这样一个属性:
plots: [
{
...
viewLevel: {
level: 'country',
adcode: '100000',
},
没错,在plots里有一个viewLevel即展示层级,这里可以选择国家,省份,城市,区县,adcode则为对应的标号这个标号其实就是邮编,如果是省份需要找到显示城市的邮编,这些都能查到,合肥是340000,替换一下这里的属性就可以了。
实现到这一步基本上就算成功了,但是怎样实现最终效果的透明底色绿色边呢,那就是咱们前端都熟悉css啦,将背景色设置为任意透明颜色,配合style属性修改样式设置边框等就完成了。
城市点的实现
接下来是数据的问题,按照参考图效果,我希望给安徽各个城市用一个点标志出来,并且原点的大小是一致的。官方样例可以看到有大小随机的点分布在各个省份的省会城市上,它的代码是这样的:
fetch(`https://gw.alipayobjects.com/os/alisis/geo-data-v0.1.1/administrative-data/area-list.json`)
.then((response) => response.json())
.then((list) => {
const data = list
.filter(({ level }) => level === 'province')
.map((item) => Object.assign({}, item, { value: Math.random() * 5000 }));
获取到一个区域的json,判断这段json里面的级别如果是‘province’省份级别就随机一个大小的圆。分析一下这个json的大概格式
可以发现,城市区县级别的值都有一个parent属性,这个属性表明了这个地点属于哪一个省份,那么我的操作很简单了,我只需要判断parent的值是340000就表示这个城市是属于安徽的,然后给它们一个相同的圆点就好了。
const data = list
.filter(({ parent }) => parent === 340000)
.map((item) => Object.assign({}, item, { value: 1500 }));
剩下的完善一下细节就好了。
tooltip的修改
最终要实现的效果是鼠标移入有一个浮动框里面展示很多的信息,这里便用到了官方的tooltip属性,但官方自带的属性是实现不了这样的效果的
这里便需要自己手动该改造了,在又看了官方文档等之后发现了可以自定义tooltip:
tooltip: {
offsets: [0, 100],
anchor: "center",
items: [
{ field: "name", alias: "名称" },
{ field: "value", alias: "随机数值" },
],
customContent(e, data) {
return domTooltip(data);
},
anchor是锚点的位置也就是tooltip展示的位置。一般设置为中间,offsets用来设置tooltip距离锚点的偏移值可以微调最终tooltip显示的位置,像我的这个展示比较长,就要适当的往上偏一点。items是需要展示的数据值,这里我暂时还没用到。customContent就比较关键了,这里返回一个函数,函数里可以通过字符串拼接等形式放入自己定义的展示板,我这里目前还都是假数据:
const domTooltip = (items) => {
return `
182.23.45.12
端口
22
FTP
HTTP
8000
FTP
HTTP
FTP
HTTP
操作
Apache 2.1.1
Discuz 2.3x
历史事件
-
2009年5月
-
2010年4月
十有九人堪白眼,百无一用是书生
查看更多
`;
};
tooltip的位置
到这里基本实现所有的功能,但是如果此时你把鼠标移到黄山这些偏视口下一点的城市你会发现tooltip展示不全,你需要通过拖动鼠标把城市往上挪一挪才能显示全。
这显然是不合理的,这个时候就需要用到L7plot自带的一些事件了,继续通过查阅文档我找到了解决办法,利用官方的mousemove事件,在鼠标移入时获取鼠标自身在视口的坐标,这个值通过返回的point属性可以获取,同时获得tooltip相对于视口的坐标,这个坐标值在官方返回的属性里plot.plots[1].tooltip.options.offsets,offsets这个数组中第一项表示x轴位置,第二项表示y轴位置,因为我的视口宽度是固定的600px,所以只要判断鼠标的落点x值大于300就让tooltip向左偏,否则都是向右偏。
plot.on("mousemove", (event) => {
event.point.x > 300
? (plot.plots[1].tooltip.options.offsets[0] = -260)
: (plot.plots[1].tooltip.options.offsets[0] = 20);
plot.plots[1].tooltip.options.offsets[1] = event.point.y;
});
最终就可以实现一个简易的判断了。
不足之处
页面初次加载的时候因为延迟,安徽地图会处于一个很小的位置,待完全加载完成后才能显示,这个后期可以通过加个loading异步什么的简单处理。
因为是地图形式,所以会显示出国境线,这个疑问并不影响整体展示暂时没去想怎么去掉,我也不知道能不能去掉。
完整代码