Echarts創建中國3D地圖
客戶需要做一版3D中國地圖,地圖要傾斜角度,然後可以支持點擊省份,對地圖兩側的圖表數據進行切換,此外還有一些紋理,頂牌信息面板的效果,不一一贅述,末尾我會放一張成品的圖片。
地圖數據
Echarts官網遷移後,地圖的json數據已經不翼而飛了,所以在開發地圖圖表時,要先找一份地圖的json數據,阿里的網站就有,直接去下載就好了。
鏈接:http://datav.aliyun.com/portal/school/atlas/area_selector
json下載完成後,要在Echarts對象初始化的時候註冊一下,代碼如下:
import china from '../../../public/mock/map/china.json' // 導入地圖json數據
import * as echarts from 'echarts'
echarts.registerMap('china', china)
組件代碼
<!-- 點線 -->
<template>
<div class="container">
<!-- <div class="bg-up"></div> -->
<div class="bg"></div>
<video
id="v1"
class="video-bg"
autoplay
loop
muted
width="7600"
height="2160"
style="object-fit: fill;width: 7600px;height: 2160px;"
>
<source :src="videos[videoUrl]" type="video/mp4" />
</video>
<div
id="echarts"
ref="echartRef"
class="map"
:style="{ width: config.width + 'px', height: config.height + 'px' }"
></div>
<div class="nine-line"></div>
</div>
</template>
<script>
import china from '../../../public/mock/map/china.json'
import * as echarts from 'echarts'
import tietu1 from '../../assets/imgs/home/123.jpg'
import video from '../../assets/imgs/map/map_bg.mp4'
import video2 from '../../assets/imgs/map/map_bg2.mp4'
import video3 from '../../assets/imgs/map/map_bg3.mp4'
import video4 from '../../assets/imgs/map/map_bg4.mp4'
import markerImg from '../../assets/imgs/map/3d_marker_bg.png'
import axiosAPI from '../../axios'
import { useModule } from '../../store/useModule'
export default {
props: {
config: {
type: Object,
default: () => ({ width: 7600, height: 2160 }),
},
},
setup() {
const moduleStore = useModule()
let dataMaps = [
{ name: '北京',
value: 457,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '天津',
value: 120,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '河北',
value: 421,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '山西',
value: 413,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '內蒙古',
value: 484,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '遼寧',
value: 106,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '吉林',
value: 18,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '黑龍江',
value: 98,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '上海',
value: 331,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '江蘇',
value: 164,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '浙江',
value: 4,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '安徽',
value: 26,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '福建',
value: 492,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '江西',
value: 48,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '山東',
value: 132,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '河南',
value: 309,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '湖北',
value: 35,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '湖南',
value: 11,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '重慶',
value: 16,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '四川',
value: 383,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '貴州',
value: 209,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '雲南',
value: 365,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '西藏',
value: 421,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '陝西',
value: 144,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '甘肅',
value: 472,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '青海',
value: 182,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '寧夏',
value: 282,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '新疆',
value: 51,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '廣東',
value: 110,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '廣西',
value: 453,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '海南',
value: 356,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '臺灣',
value: 342,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '香港',
value: 406,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
{ name: '澳門',
value: 223,
itemStyle: {
color: 'rgba(167, 204, 255, 1)',
opacity: 1,
borderWidth: 2,
borderColor: 'rgba(167, 204, 255, 1)', // 地圖邊配色
} },
]
const state = reactive({
videos: [video, video2, video3, video4],
videoUrl: window.yunyingzhihuizhongxin.videoUrl,
infoData: [],
})
const getData = async () => {
const res = await axiosAPI.get(window.yunyingzhihuizhongxin.URLs.china3D)
if (res.code === 200) {
state.infoData = res.data.data
}
}
const timer = setInterval(getData, window.yunyingzhihuizhongxin.looping.china3D * 1000)
const addcomma = (num) => (num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
const convertData = (data) => {
const res = []
for (let i = 0; i < data.length; i++) {
res.push({
name: data[i].name,
value: data[i].lonlat,
value1: data[i].value1,
value2: data[i].value2,
label1: data[i].label1,
label2: data[i].label2,
label: {
show: true,
position: 'top',
distance: 0,
backgroundColor: {
image: markerImg,
},
width: 414,
height: 176,
formatter(param) {
return `{sty1|${param.data.label1}}
{sty2|${param.data.label2}}
{sty3|${addcomma(param.data.value1)}}
{sty4|${addcomma(param.data.value2)}}`
},
rich: {
sty1: {
fontSize: 32,
padding: [15, 0, 0, 20],
color: 'rgba(23, 31, 44, 1)',
fontFamily: 'SourceHanSansCN-Bold, sans-serif',
fontWeight: 500,
align: 'left',
},
sty2: {
color: 'rgba(23, 31, 44, 1)',
padding: [-39, 20, 0, 0],
fontSize: 32,
fontFamily: 'SourceHanSansCN-Bold, sans-serif',
fontWeight: 500,
align: 'right',
},
sty3: {
fontSize: 58,
padding: [15, 0, 0, -95],
fontFamily: 'DINPro-Medium, sans-serif',
fontWeight: 500,
color: '#FFFFFF',
align: 'left',
},
sty4: {
fontSize: 58,
padding: [-70, 20, 0, 0],
fontFamily: 'DINPro-Medium, sans-serif',
fontWeight: 500,
color: '#FFFFFF',
align: 'right',
},
},
},
})
}
return res
}
let myChart
const init = () => {
if (!myChart) {
myChart = echarts.init(document.getElementById('echarts'))
echarts.registerMap('china', china)
myChart.on('click', 'series.map3D', params => {
let newOption = myChart.getOption()
newOption.geo3D[0].regions.forEach(region => {
if (params.data.name === region.name) {
if (region.itemStyle.borderWidth === 6) {
region.itemStyle.color = 'rgba(167, 204, 255, 1)'
region.itemStyle.borderColor = 'rgba(167, 204, 255, 1)'
region.itemStyle.borderWidth = 2
} else {
region.itemStyle.color = '#76ddff'
region.itemStyle.borderColor = '#76ddff'
region.itemStyle.borderWidth = 6
}
} else {
region.itemStyle.color = 'rgba(167, 204, 255, 1)'
region.itemStyle.borderColor = 'rgba(167, 204, 255, 1)'
region.itemStyle.borderWidth = 2
}
})
moduleStore.currentMapChoose === params.data.name
? moduleStore.changeCurrentMapChoose('china')
: moduleStore.changeCurrentMapChoose(params.data.name)
myChart.setOption(newOption, true)
})
}
if (moduleStore.currentMapChoose !== 'china') {
dataMaps.forEach(region => {
if (region.name === moduleStore.currentMapChoose) {
region.itemStyle.color = '#76ddff'
region.itemStyle.borderColor = '#76ddff'
region.itemStyle.borderWidth = 6
}
})
}
const option = {
visualMap: {
show: false,
symbolSize: [15, 30],
pieces: [ // 自定義每一段的範圍,以及每一段的文字
{ gte: 300, label: '1000-9999人', color: 'rgba(82, 104, 134, 0.8)' },
{ gte: 200, lte: 299, label: '500-999人', color: 'rgba(82, 104, 134, 0.8)' },
{ gte: 100, lte: 199, label: '100-499人', color: 'rgba(82, 104, 134, 0.8)' },
{ gte: 10, lte: 99, label: '10-99人', color: 'rgba(82, 104, 134, 0.8)' },
// 不指定 min,表示 min 爲無限大(-Infinity)。
{ lte: 9, label: '1-9人', color: 'rgba(82, 104, 134, 0.8)' },
],
},
geo3D: {
show: true,
type: 'map3D',
map: 'china',
regionHeight: 4,
shading: 'realistic',
height: 2800,
top: -500,
label: { // 標籤的相關設置
show: true, // (地圖上的城市名稱)是否顯示標籤 [ default: false ]
formatter(param) {
const city = (param.name).substr(0, 3)
return city === '香港' ? `{sty1|${city}}` : `{sty2|${city}}`
},
rich: {
sty1: {
color: '#ffffff',
fontSize: 28,
fontFamily: 'SourceHanSansCN-Regular, sans-serif',
align: 'right',
backgroundColor: 'transparent',
width: 120,
},
sty2: {
color: '#ffffff',
fontSize: 28,
fontFamily: 'SourceHanSansCN-Regular, sans-serif',
align: 'center',
},
},
},
roam: true,
zoom: 2,
realisticMaterial: {
detailTexture: tietu1, // 紋理貼圖
textureTiling: 1, // 紋理平鋪,1是拉伸,數字表示紋理平鋪次數
roughness: 1, // 材質粗糙度,0完全光滑,1完全粗糙
metalness: 1, // 0材質是非金屬 ,1金屬
roughnessAdjust: 0,
},
// 鼠標hover高亮
emphasis: {
label: {
show: true, // (地圖上的城市名稱)是否顯示標籤 [ default: false ]
},
itemStyle: {
areaColor: '#61A4E4',
// color: '#61A4E4',
borderColor: '#88BAEA',
borderWidth: 2,
},
},
light: { // 光照相關的設置。在 shading 爲 'color' 的時候無效。
// 光照的設置會影響到組件以及組件所在座標系上的所有圖表。合理的光照設置能夠讓整個場景的明暗變得更豐富,更有層次。
main: { // 場景主光源的設置,在 globe 組件中就是太陽光。
color: '#fff', // 主光源的顏色。[ default: #fff ]
intensity: 4, // 主光源的強度。[ default: 1 ]
shadow: false, // 主光源是否投射陰影。默認關閉。 開啓陰影可以給場景帶來更真實和有層次的光照效果。但是同時也會增加程序的運行開銷。
alpha: 150, // 主光源繞 x 軸,即上下旋轉的角度。配合 beta 控制光源的方向。[ default: 40 ]
beta: 90, // 主光源繞 y 軸,即左右旋轉的角度。[ default: 40 ]
shadowQuality: 'high',
},
ambient: { // 全局的環境光設置。
color: '#fff', // 環境光的顏色。[ default: #fff ]
intensity: 1, // 環境光的強度。[ default: 0.2 ]
},
},
viewControl: {
autoRotate: false, // 自動旋轉
// autoRotateAfterStill: 10, // 靜止時間後自動旋轉
animation: true,
alpha: 45, // 視角繞 x 軸,即上下旋轉的角度
beta: 5, // 視角繞 y 軸,即左右旋轉的角度
distance: 90, // 視角距離主體的距離
rotateSensitivity: 0,
zoomSensitivity: 0,
},
regions: dataMaps,
data: [],
// silent: true,
},
series: [
{
show: true,
type: 'map3D',
map: 'china',
regionHeight: 4,
shading: 'realistic',
itemStyle: {
opacity: 0,
},
height: 2800,
top: -500,
label: { // 標籤的相關設置
show: true, // (地圖上的城市名稱)是否顯示標籤 [ default: false ]
formatter(param) {
const city = (param.name).substr(0, 3)
return city === '香港' ? `{sty1|${city}}` : `{sty2|${city}}`
},
rich: {
sty1: {
color: '#ffffff',
fontSize: 28,
fontFamily: 'SourceHanSansCN-Regular, sans-serif',
align: 'right',
backgroundColor: 'transparent',
width: 120,
},
sty2: {
color: '#ffffff',
fontSize: 28,
fontFamily: 'SourceHanSansCN-Regular, sans-serif',
align: 'right',
height: 150,
width: 100,
backgroundColor: 'transparent',
verticalAlign: 'top',
},
},
},
roam: true,
zoom: 2,
realisticMaterial: {
detailTexture: tietu1, // 紋理貼圖
textureTiling: 1, // 紋理平鋪,1是拉伸,數字表示紋理平鋪次數
roughness: 1, // 材質粗糙度,0完全光滑,1完全粗糙
metalness: 1, // 0材質是非金屬 ,1金屬
roughnessAdjust: 0,
},
// 鼠標hover高亮
emphasis: {
label: {
show: true, // (地圖上的城市名稱)是否顯示標籤 [ default: false ]
},
itemStyle: {
areaColor: '#61A4E4',
// color: '#61A4E4',
borderColor: '#88BAEA',
borderWidth: 2,
},
},
light: { // 光照相關的設置。在 shading 爲 'color' 的時候無效。
// 光照的設置會影響到組件以及組件所在座標系上的所有圖表。合理的光照設置能夠讓整個場景的明暗變得更豐富,更有層次。
main: { // 場景主光源的設置,在 globe 組件中就是太陽光。
color: '#fff', // 主光源的顏色。[ default: #fff ]
intensity: 4, // 主光源的強度。[ default: 1 ]
shadow: false, // 主光源是否投射陰影。默認關閉。 開啓陰影可以給場景帶來更真實和有層次的光照效果。但是同時也會增加程序的運行開銷。
alpha: 150, // 主光源繞 x 軸,即上下旋轉的角度。配合 beta 控制光源的方向。[ default: 40 ]
beta: 90, // 主光源繞 y 軸,即左右旋轉的角度。[ default: 40 ]
shadowQuality: 'high',
},
ambient: { // 全局的環境光設置。
color: '#fff', // 環境光的顏色。[ default: #fff ]
intensity: 1, // 環境光的強度。[ default: 0.2 ]
},
},
viewControl: {
autoRotate: false, // 自動旋轉
// autoRotateAfterStill: 10, // 靜止時間後自動旋轉
animation: true,
alpha: 45, // 視角繞 x 軸,即上下旋轉的角度
beta: 5, // 視角繞 y 軸,即左右旋轉的角度
distance: 90, // 視角距離主體的距離
rotateSensitivity: 0,
zoomSensitivity: 0,
},
regions: dataMaps,
data: [],
zlevel: 1,
// silent: true,
},
// 疊加地圖上需要顯示的數據,插牌
{
type: 'scatter3D',
name: 'scatter3D',
coordinateSystem: 'geo3D',
symbol: 'pin',
symbolSize: 0,
label: {},
data: convertData(state.infoData),
},
],
}
// 把option設置給myChart實例
myChart.setOption(option, true)
}
watch(() => state.infoData, () => {
init()
})
// 加載完就調用的方法 vue3生命週期
onMounted(() => {
getData()
})
onBeforeUnmount(() => {
clearInterval(timer)
})
return {
...toRefs(state),
}
},
}
</script>
<style lang="scss" scoped>
.container {
position: relative;
width: 7600px;
height: 2160px;
.bg-up {
position: absolute;
top: 0;
left: 0;
z-index: 4;
width: 7600px;
height: 2160px;
@include set-back("../../assets/imgs/home/map-bg.png");
}
.bg {
position: absolute;
top: 0;
left: 0;
z-index: 3;
width: 7600px;
height: 2160px;
@include set-back("../../assets/imgs/home/8kmap_bg.png");
}
.video-bg {
position: relative;
z-index: 2;
width: 7600px;
height: 2160px;
}
.map {
position: absolute;
top: 0;
left: 0;
z-index: 4;
}
.nine-line {
position: absolute;
top: 1580px;
left: 5300px;
z-index: 5;
width: 150px;
height: 245px;
transform: scale(1.5);
@include set-back("../../assets/imgs/map/nine-line.png");
}
}
</style>
成品圖
如下所示: