最近在大屏可視化項目中有個展現地圖的功能,要求:html
獲得這個需求後第一個想到的就是Echarts,因此打開Echarts官方案例查看,找到一個相似的地圖 demojquery
查看完整代碼看到他是請求一個路徑獲取數據,反問了下路徑看到他是一串帶座標的json數據 cdn.jsdelivr.net/gh/apache/e…git
到這一步我涉及到知識盲區了,因而百度了下geoJson,發現阿里雲dataV提供一個網站能獲取geoJson數據 datav.aliyun.com/tools/atlas…github
按照demo教程,嘗試實現:web
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>echarts 3d map</title>
<style>
* {
margin: 0;
padding: 0;
}
.echarts-map {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: url("./background.png") no-repeat;
}
</style>
</head>
<body>
<div class="echarts-map" id="3dMap"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.1.2/echarts.min.js"></script>
</html>
複製代碼
let mapEcharts = null;
if (mapEcharts) {
mapEcharts.dispose(); // 銷燬實例,實例銷燬後沒法再被使用。
}
// 初始化圖表
mapEcharts = echarts.init(document.getElementById("3dMap"));
// 數據請求
// 加載效果
mapEcharts.showLoading();
$.getJSON('https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json', jsonData => {
// 註冊地圖
echarts.registerMap('china', jsonData);
// 配置
let options = {
series: [
{
name: "map",
type: "map", // 地圖
map: 'china', // 加載註冊的地圖
},
],
};
mapEcharts.setOption(options); // 實例配置項與數據
// 隱藏loading
mapEcharts.hideLoading();
})
複製代碼
這裏能夠看到,地圖基本已經出來了,接下來就是美化地圖,在地圖上能看到海南諸島的數據,可是實際上海南諸島的數據已經在右下角,因此我整理了一份數據
china.js
放在本地,各位大佬能夠在demo中自行獲取。ajax
添加地圖漸變顏色以及選中顏色apache
let options = {
series: [
{
name: "map",
type: "map", // 地圖
map: mapName, // 加載註冊的地圖
selectedMode: false, // 不讓單獨選中
roam: true, // 開始鼠標事件,scale縮放、move移動
// 圖形上的文本標籤
label: {
show: true,
color: "#000a3c",
},
// 地圖樣式
itemStyle: {
// 區域樣式
areaColor: {
type: "radial",
x: 0.5,
y: 0.5,
r: 3,
colorStops: [
{
offset: 0,
color: "rgba(223, 231, 242, 1)", // 0% 處的顏色
},
{
offset: 1,
color: "rgba(2, 99, 206, 1)", // 100% 處的顏色
},
],
globalCoord: false, // 缺省爲 false
},
borderWidth: 1, // 邊框大小
borderColor: "rgba(104, 152, 190, 1)", // 邊框樣式
shadowColor: "rgba(128, 217, 248, 1)", // 陰影顏色
shadowOffsetX: -2, // 陰影水平方向上的偏移距離
shadowOffsetY: 2, // 陰影垂直方向上的偏移距離
shadowBlur: 10, // 文字塊的背景陰影長度
},
// 選中狀態下樣式
emphasis: {
label: {
color: "#ffffff",
},
itemStyle: {
areaColor: "#a5d4fe",
},
},
},
],
};
複製代碼
let options = {
geo: {
map: mapName, //地圖類型。
zoom: 1,
roam: true,
animation: false,
itemStyle: {
// 區域樣式
areaColor: {
type: "radial",
x: 0.5,
y: 0.5,
r: 0.8,
colorStops: [
{
offset: 0,
color: "rgba(147, 235, 248, 1)", // 0% 處的顏色
},
{
offset: 1,
color: "rgba(2, 99, 206, 1)", // 100% 處的顏色
},
],
globalCoord: false, // 缺省爲 false
},
shadowColor: "#105781", //地圖區域的陰影顏色。
shadowOffsetX: 0,
shadowOffsetY: 10,
},
},
series: [
{
name: "map",
type: "map", // 地圖
map: mapName, // 加載註冊的地圖
selectedMode: false, // 不讓單獨選中
roam: true, // 開始鼠標事件,scale縮放、move移動
// 圖形上的文本標籤
label: {
show: true,
color: "#000a3c",
},
// 地圖樣式
itemStyle: {
// 區域樣式
areaColor: {
type: "radial",
x: 0.5,
y: 0.5,
r: 3,
colorStops: [
{
offset: 0,
color: "rgba(223, 231, 242, 1)", // 0% 處的顏色
},
{
offset: 1,
color: "rgba(2, 99, 206, 1)", // 100% 處的顏色
},
],
globalCoord: false, // 缺省爲 false
},
borderWidth: 1, // 邊框大小
borderColor: "rgba(104, 152, 190, 1)", // 邊框樣式
shadowColor: "rgba(128, 217, 248, 1)", // 陰影顏色
shadowOffsetX: -2, // 陰影水平方向上的偏移距離
shadowOffsetY: 2, // 陰影垂直方向上的偏移距離
shadowBlur: 10, // 文字塊的背景陰影長度
},
// 選中狀態下樣式
emphasis: {
label: {
color: "#ffffff",
},
itemStyle: {
areaColor: "#a5d4fe",
},
},
},
],
};
複製代碼
能夠看到如今已經有一個相似3d的陰影效果json
mapEcharts.on("click", (params) => {
// 當雙擊事件發生時,清除單擊事件,僅響應雙擊事件
clearTimeout(timeFn);
timeFn = setTimeout(function () {
if (
allAreaCode.filter((item) => item.name.indexOf(params.name) > -1)[0]
) {
let areaCode = allAreaCode.filter(
(item) => item.name.indexOf(params.name) > -1
)[0].code;
loadMap(
`https://geo.datav.aliyun.com/areas_v3/bound/${areaCode}_full.json`
)
.then((data) => {
initMap(data, areaCode);
})
.catch(() => {
loadMap(
`https://geo.datav.aliyun.com/areas_v3/bound/${areaCode}.json`
)
.then((res) => {
initMap(res, areaCode);
})
.catch(() => {});
});
historyList.push({
code: areaCode,
name: params.name,
});
let result = [];
let obj = {};
for (let i = 0; i < historyList.length; i++) {
if (!obj[historyList[i].code]) {
result.push(historyList[i]);
obj[historyList[i].code] = true;
}
}
historyList = result;
}
}, 250);
});
複製代碼
這裏的loadMap
爲數據請求封裝,initMap
爲渲染地圖,後面在所有代碼中會放出來。數組
這裏主要邏輯是監聽地圖點擊事件,經過篩選的到areaCode
,經過areaCode
拼接路徑請求數據,這裏的地址能在上面網站獲取,之因此分_full
和普通的是由於,地圖數據有可能包含子區域數據,例如廣東省內包含廣州市、佛上市等......markdown
allAreaCode
是我花不少時間蒐集全國地區省市區code數據,這份數據可能有部分錯誤,懂爬蟲的大佬能夠直接爬上面網站的數據,處理成個人數據格式就行
地圖渲染後把歷史記錄推動historyList
中,後面鑽出用到。
mapEcharts.on("dblclick", (params) => {
// 當雙擊事件發生時,清除單擊事件,僅響應雙擊事件
clearTimeout(timeFn);
if (historyList.length == 1) {
alert("已經到達最上一級地圖了");
return;
}
let map = historyList.pop();
if (historyList[historyList.length - 1].code == "china") {
initMap(china, "china", "中國");
} else {
loadMap(
`https://geo.datav.aliyun.com/areas_v3/bound/${
historyList[historyList.length - 1].code
}_full.json`
).then((data) => {
initMap(data, historyList[historyList.length - 1].code);
}).catch(() => {
loadMap(
`https://geo.datav.aliyun.com/areas_v3/bound/${
historyList[historyList.length - 1].code
}.json`
)
.then((res) => {
initMap(res, historyList[historyList.length - 1].code);
})
.catch(() => {});
});
}
});
複製代碼
這裏採用雙擊鑽出方式,雙擊時從歷史記錄中刪除最後一個,而且取刪除後數組的最後一個元素數據來渲染地圖
若是開啓鼠標事件,會發現不管scale或者move,map層和geo層都是分開的,例如:
這種狀況咱們能夠經過事件監聽來同步他們的數據
mapEcharts.on("georoam", (params) => {
let option = mapEcharts.getOption(); //得到option對象
if (params.zoom != null && params.zoom != undefined) {
//捕捉到縮放時
option.geo[0].zoom = option.series[0].zoom; //下層geo的縮放等級跟着上層的geo一塊兒改變
option.geo[0].center = option.series[0].center; //下層的geo的中心位置隨着上層geo一塊兒改變
} else {
//捕捉到拖曳時
option.geo[0].center = option.series[0].center; //下層的geo的中心位置隨着上層geo一塊兒改變
}
mapEcharts.setOption(option); //設置option
});
複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>echarts 3d map</title>
<style>
* {
margin: 0;
padding: 0;
}
.echarts-map {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: url("./background.png") no-repeat;
}
</style>
</head>
<body>
<div class="echarts-map" id="3dMap"></div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.1.2/echarts.min.js"></script>
<script src="./china.js"></script>
<script src="./geoAtlasJson.js"></script>
<script>
let mapEcharts = null;
let historyList = [];
let timeFn = null;
if (mapEcharts) {
mapEcharts.dispose(); // 銷燬實例,實例銷燬後沒法再被使用。
}
// 初始化圖表
mapEcharts = echarts.init(document.getElementById("3dMap"));
historyList.push({
code: "china",
name: "中國",
});
// 加載效果
mapEcharts.showLoading();
initMap(china, "china", "中國");
mapEcharts.on("click", (params) => {
// 當雙擊事件發生時,清除單擊事件,僅響應雙擊事件
clearTimeout(timeFn);
timeFn = setTimeout(function () {
if (
allAreaCode.filter((item) => item.name.indexOf(params.name) > -1)[0]
) {
let areaCode = allAreaCode.filter(
(item) => item.name.indexOf(params.name) > -1
)[0].code;
loadMap(
`https://geo.datav.aliyun.com/areas_v3/bound/${areaCode}_full.json`
)
.then((data) => {
initMap(data, areaCode);
})
.catch(() => {
loadMap(
`https://geo.datav.aliyun.com/areas_v3/bound/${areaCode}.json`
)
.then((res) => {
initMap(res, areaCode);
})
.catch(() => {});
});
historyList.push({
code: areaCode,
name: params.name,
});
let result = [];
let obj = {};
for (let i = 0; i < historyList.length; i++) {
if (!obj[historyList[i].code]) {
result.push(historyList[i]);
obj[historyList[i].code] = true;
}
}
historyList = result;
}
}, 250);
});
mapEcharts.on("dblclick", (params) => {
// 當雙擊事件發生時,清除單擊事件,僅響應雙擊事件
clearTimeout(timeFn);
if (historyList.length == 1) {
alert("已經到達最上一級地圖了");
return;
}
let map = historyList.pop();
console.log(historyList[historyList.length - 1])
if (historyList[historyList.length - 1].code == "china") {
initMap(china, "china", "中國");
} else {
loadMap(
`https://geo.datav.aliyun.com/areas_v3/bound/${
historyList[historyList.length - 1].code
}_full.json`
)
.then((data) => {
initMap(data, historyList[historyList.length - 1].code);
})
.catch(() => {
loadMap(
`https://geo.datav.aliyun.com/areas_v3/bound/${
historyList[historyList.length - 1].code
}.json`
)
.then((res) => {
initMap(res, historyList[historyList.length - 1].code);
})
.catch(() => {});
});
}
});
mapEcharts.on("georoam", (params) => {
let option = mapEcharts.getOption(); //得到option對象
if (params.zoom != null && params.zoom != undefined) {
//捕捉到縮放時
option.geo[0].zoom = option.series[0].zoom; //下層geo的縮放等級跟着上層的geo一塊兒改變
option.geo[0].center = option.series[0].center; //下層的geo的中心位置隨着上層geo一塊兒改變
} else {
//捕捉到拖曳時
option.geo[0].center = option.series[0].center; //下層的geo的中心位置隨着上層geo一塊兒改變
}
mapEcharts.setOption(option); //設置option
});
// 地圖數據請求
async function loadMap(url, pathName) {
return await $.getJSON(url);
}
// 地圖初始化
function initMap(mapData, mapName) {
// 註冊地圖
echarts.registerMap(mapName, mapData);
// 配置項
let options = {
geo: {
map: mapName, //地圖類型。
zoom: 1,
roam: true,
animation: false,
itemStyle: {
// 區域樣式
areaColor: {
type: "radial",
x: 0.5,
y: 0.5,
r: 0.8,
colorStops: [
{
offset: 0,
color: "rgba(147, 235, 248, 1)", // 0% 處的顏色
},
{
offset: 1,
color: "rgba(2, 99, 206, 1)", // 100% 處的顏色
},
],
globalCoord: false, // 缺省爲 false
},
shadowColor: "#105781", //地圖區域的陰影顏色。
shadowOffsetX: 0,
shadowOffsetY: 10,
},
},
series: [
{
name: "map",
type: "map", // 地圖
map: mapName, // 加載註冊的地圖
selectedMode: false, // 不讓單獨選中
roam: true, // 開始鼠標事件,scale縮放、move移動
// 圖形上的文本標籤
label: {
show: true,
color: "#000a3c",
},
// 地圖樣式
itemStyle: {
// 區域樣式
areaColor: {
type: "radial",
x: 0.5,
y: 0.5,
r: 3,
colorStops: [
{
offset: 0,
color: "rgba(223, 231, 242, 1)", // 0% 處的顏色
},
{
offset: 1,
color: "rgba(2, 99, 206, 1)", // 100% 處的顏色
},
],
globalCoord: false, // 缺省爲 false
},
borderWidth: 1, // 邊框大小
borderColor: "rgba(104, 152, 190, 1)", // 邊框樣式
shadowColor: "rgba(128, 217, 248, 1)", // 陰影顏色
shadowOffsetX: -2, // 陰影水平方向上的偏移距離
shadowOffsetY: 2, // 陰影垂直方向上的偏移距離
shadowBlur: 10, // 文字塊的背景陰影長度
},
// 選中狀態下樣式
emphasis: {
label: {
color: "#ffffff",
},
itemStyle: {
areaColor: "#a5d4fe",
},
},
},
],
};
mapEcharts.setOption(options); // 實例配置項與數據
// 隱藏loading
mapEcharts.hideLoading();
}
</script>
</html>
複製代碼
demo github地址:github.com/ljnMeow/ech…
在製做這份demo時實際上是花了挺多時間的,最開始是想經過echarts-gl
直接實現一個3d地圖,但經過官方文檔來看echarts-gl
的map3d
好像不支持漸變背景,因此就直接放棄了,其次就是蒐集省市區全部數據花費的時間,不得不感嘆咱們大華夏是真的地大物博。
最後但願這份教程對各位有幫助,以爲ok的話麻煩留個贊👍,或者github給點star🌟也行🙏。
祝各位大佬步步高昇🧨