發(fā) ECharts 中國(guó)地圖:數(shù)據(jù)加載到交互實(shí)現(xiàn)全攻略)
搞數(shù)據(jù)可視化的人幾乎都會(huì)遇到一個(gè)需求給領(lǐng)導(dǎo)做一個(gè)中國(guó)地圖點(diǎn)上幾個(gè)城市、標(biāo)一些數(shù)據(jù)做成大屏投上去。我第一次在Vue項(xiàng)目里做中國(guó)地圖的時(shí)候裝好echarts、寫(xiě)好option頁(yè)面上一片空白折騰了半天才發(fā)現(xiàn)是地圖數(shù)據(jù)沒(méi)加載。后來(lái)做多了才明白這個(gè)需求看著簡(jiǎn)單其實(shí)坑都在細(xì)節(jié)里——數(shù)據(jù)怎么拿、地圖怎么注冊(cè)、交互怎么綁、打包了為什么又白屏每一個(gè)環(huán)節(jié)都能卡住人。這篇內(nèi)容就是把我實(shí)際趟過(guò)的路整理一遍從零開(kāi)始在Vue 3 Vite項(xiàng)目里接入ECharts實(shí)現(xiàn)一個(gè)帶Tooltip提示、區(qū)域點(diǎn)擊、數(shù)據(jù)分級(jí)著色、散點(diǎn)標(biāo)注的中國(guó)地圖。適合剛接觸ECharts沒(méi)多久、或者已經(jīng)會(huì)畫(huà)折線圖但第一次搞地圖的開(kāi)發(fā)者。文章不會(huì)只貼代碼每個(gè)關(guān)鍵步驟我都會(huì)說(shuō)明白為什么這么做以及最常見(jiàn)的報(bào)錯(cuò)和排查思路。1. 開(kāi)工前必須搞清楚兩件事版本選擇和數(shù)據(jù)來(lái)源1.1 Vue項(xiàng)目環(huán)境搭建與ECharts依賴(lài)安裝這一步?jīng)]什么懸念直接用Vite初始化一個(gè)Vue 3項(xiàng)目就行。很多人還在用Vue CLI但新項(xiàng)目真的沒(méi)必要了Vite啟動(dòng)快、配置少、社區(qū)生態(tài)也成熟。npm create vitelatest china-map-demo -- --template vue cd china-map-demo npm install裝完基礎(chǔ)依賴(lài)之后裝上ECharts本體。這里有一個(gè)選擇裝全量包還是按需引入我的建議是——先裝全量包跑通了再優(yōu)化性能。npm install echarts --save很多人一上來(lái)就搞按需引入echarts/core、echarts/charts、echarts/components一個(gè)個(gè)引結(jié)果漏了某個(gè)組件地圖渲染不出來(lái)排查半天才找到原因。首次做地圖別給自己加戲全量引入最穩(wěn)。項(xiàng)目做完如果打包體積確實(shí)超標(biāo)一般超過(guò)500KB你會(huì)有感覺(jué)的再回頭做按需引入那時(shí)候你已經(jīng)知道哪些模塊是必須的了。1.2 ECharts 5.x最大的坑官方包不再內(nèi)置中國(guó)地圖數(shù)據(jù)這是新手最容易踩的坑也是網(wǎng)上很多舊教程失效的根本原因。在ECharts 4.x及更早的版本里echarts/map/json/china.json是直接放在npm包里的import china from echarts/map/json/china.json就能用。但從ECharts 5.0開(kāi)始官方把地圖數(shù)據(jù)從默認(rèn)包里移除了你裝完echarts之后node_modules里根本沒(méi)有中國(guó)地圖數(shù)據(jù)文件。所以無(wú)論你在代碼里怎么寫(xiě)頁(yè)面都會(huì)是空白的——不是代碼錯(cuò)了是數(shù)據(jù)源不存在。這是很多人第一次做地圖白屏的頭號(hào)原因。解決方案有兩個(gè)方向從第三方接口實(shí)時(shí)加載GeoJSON數(shù)據(jù)下載GeoJSON文件放在項(xiàng)目里使用這兩種方案各有適用場(chǎng)景下一章詳細(xì)展開(kāi)。2. 中國(guó)地圖GeoJSON數(shù)據(jù)的獲取思路與選擇理由2.1 可行方案對(duì)比容易踩坑的舊路子先說(shuō)說(shuō)我試過(guò)的幾個(gè)方案給后來(lái)人排排雷。方案一找npm包網(wǎng)上能搜到china-map、echarts-map-china之類(lèi)的包但大部分都是多年沒(méi)更新的老包數(shù)據(jù)精度低不說(shuō)很多還只有省級(jí)邊界沒(méi)有市級(jí)數(shù)據(jù)維護(hù)狀態(tài)堪憂。更重要的是這些包質(zhì)量參差不齊有些甚至還需要配合舊版ECharts才能跑。我不建議在正經(jīng)項(xiàng)目里用這種包。方案二從第三方CDN引JS文件https://cdn.jsdelivr.net/npm/echarts4.9.0/map/json/china.json這個(gè)地址用的是4.x的包能拿到數(shù)據(jù)但有兩個(gè)麻煩一是你的項(xiàng)目就依賴(lài)了外網(wǎng)CDN內(nèi)網(wǎng)部署或者外網(wǎng)波動(dòng)時(shí)地圖就掛了二是這個(gè)文件和當(dāng)今常用需求的適配度一般行政區(qū)劃可能不是最新的。方案三阿里云DataV GeoJSON接口推薦阿里云DataV提供了一套非常良心的GeoJSON數(shù)據(jù)接口支持全國(guó)、省、市、區(qū)縣各級(jí)邊界數(shù)據(jù)而且持續(xù)在更新。關(guān)鍵是它返回的就是標(biāo)準(zhǔn)的GeoJSON格式ECharts的registerMap直接就能用。地址格式如下https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json其中100000_full代表全國(guó)范圍含南海諸島如果是某個(gè)省份就用對(duì)應(yīng)的行政區(qū)劃代碼比如廣東省是440000_full直接請(qǐng)求就能拿到數(shù)據(jù)。這個(gè)接口我用了很久穩(wěn)定性相當(dāng)不錯(cuò)。2.2 我為什么選擇運(yùn)行時(shí)請(qǐng)求GeoJSON而不是下載JSON文件針對(duì)中國(guó)地圖這個(gè)場(chǎng)景我一開(kāi)始也是先把JSON下載到本地放static里后來(lái)發(fā)現(xiàn)運(yùn)行時(shí)請(qǐng)求反而更省事。原因有兩個(gè)第一國(guó)家測(cè)繪部門(mén)對(duì)行政區(qū)劃有調(diào)整省市邊界偶爾會(huì)變化。如果文件放在本地打包一旦數(shù)據(jù)更新你得重新發(fā)版本。用接口請(qǐng)求的話服務(wù)端數(shù)據(jù)更新了客戶端刷新就拿到了。第二如果項(xiàng)目本身就是前后端分離部署地圖數(shù)據(jù)和項(xiàng)目一起打包也不存在跨域問(wèn)題。如果走接口記得在開(kāi)發(fā)環(huán)境配一下Vite的proxy代理避免跨域報(bào)錯(cuò)。所以我最終的策略是生產(chǎn)環(huán)境優(yōu)先用DataV的接口請(qǐng)求如果網(wǎng)絡(luò)要求嚴(yán)苛比如政府內(nèi)網(wǎng)再在構(gòu)建的時(shí)候把JSON文件拉下來(lái)放到靜態(tài)資源目錄里。這兩種方案在代碼層面完全兼容只是數(shù)據(jù)來(lái)源不同后面會(huì)給出具體寫(xiě)法。3. 從空頁(yè)面到地圖渲染完成的核心開(kāi)發(fā)步驟3.1 初始化ECharts實(shí)例時(shí)最容易忽略的容器問(wèn)題用Vue做ECharts第一步是先有一個(gè)DOM容器。這里有個(gè)細(xì)節(jié)經(jīng)常坑人ECharts初始化時(shí)要求容器必須已經(jīng)有確定的寬度和高度。如果你在onMounted里初始化但容器父級(jí)用了flex布局且沒(méi)有給高度初始化出來(lái)的實(shí)例寬度會(huì)是0圖表直接不顯示。template div classmap-container div refchartRef classchart-box/div /div /template style scoped .map-container { width: 100%; height: 600px; } .chart-box { width: 100%; height: 100%; } /style容器樣式必須明確高度這是一個(gè)老生常談但永遠(yuǎn)有人踩的坑。600px只是示例實(shí)際項(xiàng)目中大屏一般用calc(100vh - 某個(gè)值)來(lái)適配。初始化實(shí)例的代碼在script setup里這樣寫(xiě)import * as echarts from echarts import { ref, onMounted, onUnmounted } from vue // 注意這里不要用 reactive 包裹 chart 實(shí)例 const chartRef ref(null) let chart null onMounted(() { chart echarts.init(chartRef.value) renderMap() }) function renderMap() { // 請(qǐng)求地圖數(shù)據(jù)注冊(cè)地圖設(shè)置option fetchChinaMapData().then(chinaJson { echarts.registerMap(china, chinaJson) chart.setOption({ // 詳細(xì)配置見(jiàn)3.3節(jié) }) }) }這里有兩個(gè)關(guān)鍵點(diǎn)一是chart變量不能放進(jìn)reactive或者ref里。因?yàn)閂ue的響應(yīng)式系統(tǒng)會(huì)遞歸代理對(duì)象而ECharts實(shí)例內(nèi)部有大量復(fù)雜對(duì)象被代理后可能出現(xiàn)性能問(wèn)題甚至異常行為。我見(jiàn)過(guò)有人把chart放進(jìn)reactive里結(jié)果chart.setOption直接報(bào)TypeError的情況。實(shí)例就用普通變量保存它不需要響應(yīng)式。二是echarts.init的時(shí)機(jī)必須在DOM渲染完成后。onMounted里執(zhí)行是沒(méi)問(wèn)題的但如果是在nextTick之前訪問(wèn)了chartRef.value拿到的是null。理論上onMounted時(shí)DOM已經(jīng)掛載但保險(xiǎn)起見(jiàn)如果DOM是v-if控制的要在v-if變?yōu)閠rue之后的下一個(gè)tick再初始化。3.2 地圖數(shù)據(jù)請(qǐng)求與注冊(cè)的標(biāo)準(zhǔn)寫(xiě)法請(qǐng)求GeoJSON我直接用fetch不額外引axios了一個(gè)文件里的邏輯能少則少。const GEO_URL https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json async function fetchChinaMapData() { const response await fetch(GEO_URL) if (!response.ok) { throw new Error(地圖數(shù)據(jù)請(qǐng)求失敗${response.status}) } return await response.json() }注冊(cè)地圖和數(shù)據(jù)請(qǐng)求要放在同一個(gè)異步流程里拿到JSON后先registerMap再setOption。順序不能反先注冊(cè)、后使用這是API設(shè)計(jì)上的要求反了會(huì)報(bào)錯(cuò)。async function renderMap() { const chinaJson await fetchChinaMapData() echarts.registerMap(china, chinaJson) chart.setOption(createMapOption()) }有一點(diǎn)要特別注意如果項(xiàng)目里要加載多個(gè)層級(jí)的地圖比如點(diǎn)擊某個(gè)省再下鉆到這個(gè)省的地圖不同層級(jí)的JSON對(duì)應(yīng)不同的GeoJSON但registerMap的第二個(gè)參數(shù)每次都是新的。你可以多次注冊(cè)同名地圖registerMap(china, newData)會(huì)覆蓋舊的但要注意時(shí)序問(wèn)題——上一次異步請(qǐng)求還沒(méi)回來(lái)下一次請(qǐng)求又發(fā)出去了響應(yīng)順序顛倒會(huì)導(dǎo)致顯示錯(cuò)誤。后續(xù)章節(jié)會(huì)講怎么處理這種競(jìng)態(tài)。3.3 一個(gè)能直接跑起來(lái)的基礎(chǔ)Option配置先給一個(gè)最基礎(chǔ)的中國(guó)地圖option后面的交互和美化都從這個(gè)基礎(chǔ)往上疊加。function createMapOption() { return { tooltip: { trigger: item, formatter: function(params) { return ${params.name}br/數(shù)值${params.value || 0} } }, visualMap: { min: 0, max: 1000, left: 20, bottom: 20, calculable: true, inRange: { color: [#e0f3f8, #abd9e9, #74add1, #4575b4, #313695] } }, series: [ { name: 示例數(shù)據(jù), type: map, map: china, roam: true, label: { show: true, fontSize: 10 }, data: [ { name: 北京, value: 380 }, { name: 上海, value: 520 }, { name: 廣東, value: 860 } ] } ] } }roam: true是開(kāi)啟地圖縮放和平移的開(kāi)關(guān)這個(gè)后面做交互的時(shí)候會(huì)用到。visualMap在ECharts 5.x里用起來(lái)比4.x順手多了計(jì)算邏輯清晰顏色漸變也能直接配。這里說(shuō)一個(gè)新手容易懵的點(diǎn)series里的data數(shù)組每一項(xiàng)的name必須和GeoJSON里的name字段完全一致才能正確匹配到對(duì)應(yīng)區(qū)域。不同數(shù)據(jù)源返回的GeoJSONname字段可能是北京市也可能是北京如果對(duì)不上數(shù)據(jù)不會(huì)報(bào)錯(cuò)但就是不顯示或者顯示成未找到。排查這種問(wèn)題最快的方式是console.log(chinaJson.features.map(f f.properties.name))先看看數(shù)據(jù)源里到底叫啥名字。4. 交互功能配置Tooltip、點(diǎn)擊事件與視覺(jué)映射4.1 Tooltip提示框的自定義格式化Tooltip是用戶感知交互的第一步默認(rèn)樣式太樸素而且很擋視線。做地圖大屏的時(shí)候我一般會(huì)把背景改成半透明深色文字加陰影位置跟隨鼠標(biāo)偏移。tooltip: { trigger: item, backgroundColor: rgba(0, 0, 0, 0.7), borderColor: #333, borderWidth: 1, textStyle: { color: #fff, fontSize: 13 }, formatter: function(params) { if (!params.name) return return div stylefont-weight:bold;margin-bottom:4px;${params.name}/div div數(shù)值${params.value || 暫無(wú)數(shù)據(jù)}/div } }formatter里可以返回HTML字符串這給了很大的自由度。實(shí)際項(xiàng)目中你完全可以在Tooltip里加一個(gè)縮略圖、放一段排行列表、甚至塞一個(gè)迷你進(jìn)度條——只要返回的字符串是合法HTMLECharts就會(huì)渲染出來(lái)。4.2 區(qū)域點(diǎn)擊事件綁定方式與冒泡陷阱地圖點(diǎn)擊是最常用的交互。比如大屏場(chǎng)景里點(diǎn)擊某個(gè)省要跳轉(zhuǎn)到該省的詳情頁(yè)或者觸發(fā)一個(gè)下鉆請(qǐng)求。ECharts的做法是給實(shí)例綁定click事件chart.on(click, function(params) { // params.name 就是省份名稱(chēng) // params.value 就是該省份的數(shù)值 // params.region 在某些場(chǎng)景下有額外信息 console.log(點(diǎn)擊了, params.name, params.value) })注意別把事件綁到window或者容器DOM上ECharts的事件都在chart實(shí)例上。另外如果圖表區(qū)域同時(shí)有g(shù)eo和series-map兩種坐標(biāo)系疊放事件可能觸發(fā)兩次——這是雙層繪制的經(jīng)典問(wèn)題后面會(huì)細(xì)說(shuō)。如果要做省份下鉆點(diǎn)擊事件里拿到params.name之后用對(duì)應(yīng)的行政區(qū)劃代碼去請(qǐng)求下一級(jí)GeoJSON重新注冊(cè)地圖并渲染即可。這種下鉆交互給用戶的體驗(yàn)提升是肉眼可見(jiàn)的也是地圖可視化中最常用的進(jìn)階功能。4.3 visualMap實(shí)現(xiàn)按數(shù)值分級(jí)著色visualMap是讓地圖有信息量的核心組件它把數(shù)值映射成顏色一眼就能看出分布規(guī)律。上面基礎(chǔ)配置里已經(jīng)用了連續(xù)型visualMaptype默認(rèn)為continuous這里補(bǔ)充幾個(gè)關(guān)鍵參數(shù)的解釋min和max數(shù)據(jù)范圍建議手動(dòng)設(shè)置范圍不對(duì)的話顏色分布會(huì)失真calculable: true顯示拖拽手柄用戶能手動(dòng)篩選區(qū)間很有交互感inRange.color顏色數(shù)組從低到高排列選色時(shí)注意色盲友好性text左右兩端的文字標(biāo)簽比如[高, 低]注意數(shù)組順序和視覺(jué)方向匹配visualMap: { min: 0, max: 1000, left: 20, bottom: 20, calculable: true, text: [高, 低], inRange: { color: [#e0f3f8, #abd9e9, #74add1, #4575b4, #313695] } }這里有個(gè)常見(jiàn)問(wèn)題如果某幾個(gè)省份沒(méi)有數(shù)據(jù)會(huì)顯示成未定義的顏色通常是灰色。如果你希望無(wú)數(shù)據(jù)省份顯示為特定顏色比如淺灰可以給對(duì)應(yīng)省份的data項(xiàng)只傳name不傳value然后在visualMap里配置visualMap.inRange之外再通過(guò)itemStyle的areaColor給無(wú)數(shù)據(jù)地區(qū)一個(gè)兜底色。比如series: [ { type: map, map: china, itemStyle: { areaColor: #f0f0f0 } } ]這個(gè)areaColor會(huì)被visualMap覆蓋——只要某個(gè)區(qū)域有value就按visualMap映射顏色沒(méi)有value的區(qū)域就顯示areaColor。這個(gè)組合邏輯非常實(shí)用。5. 我踩過(guò)的幾個(gè)大坑和完整排查鏈路5.1 Vue響應(yīng)式把ECharts實(shí)例變成代理對(duì)象這個(gè)坑我印象深刻。剛開(kāi)始做的時(shí)候我想著chart實(shí)例可能存在reactive({ chart: null })里方便訪問(wèn)結(jié)果一執(zhí)行chart.setOption就報(bào)錯(cuò)錯(cuò)誤信息亂七八糟一看就是內(nèi)部方法調(diào)用出了問(wèn)題。原因是Vue 3的reactive會(huì)對(duì)對(duì)象進(jìn)行Proxy代理ECharts實(shí)例內(nèi)部有大量循環(huán)引用、類(lèi)實(shí)例和DOM引用被Proxy處理之后行為變得不可預(yù)期甚至訪問(wèn)某些方法時(shí)報(bào)錯(cuò)。排查思路也很簡(jiǎn)單寫(xiě)一句話測(cè)試一下console.log(chart)是不是Proxy。如果是在實(shí)例化之后立刻toRaw()還原或者干脆別用響應(yīng)式包裹。我最終的方案是let chart null // 普通變量不放進(jìn)reactive不要試圖把ECharts實(shí)例放進(jìn)ref、reactive、computed等任何響應(yīng)式容器里。記住一條原則ECharts實(shí)例只跟DOM容器有關(guān)系跟Vue數(shù)據(jù)流沒(méi)關(guān)系。5.2 地圖顯示成南海諸島或者白屏的排查鏈路白屏問(wèn)題前面講了八成的可能性是地圖數(shù)據(jù)沒(méi)加載。這里補(bǔ)充另一個(gè)變種地圖顯示了但只看到一個(gè)南海諸島全國(guó)輪廓完全看不到或者整個(gè)地圖縮在右下角。這通常是因?yàn)榈貓D的GeoJSON和你設(shè)置的坐標(biāo)系不匹配。DataV返回的GeoJSON默認(rèn)坐標(biāo)是WGS84或GCJ02如果項(xiàng)目里之前做過(guò)地圖初始化、設(shè)置了geo: { map: china, aspectScale: ... }之類(lèi)的高級(jí)參數(shù)坐標(biāo)系或縮放比例不同會(huì)導(dǎo)致顯示異常。排查辦法是一層一層拆注釋掉所有g(shù)eo配置只保留series.map臨時(shí)寫(xiě)入一個(gè)靜態(tài)GeoJSON文件比如從DataV下載下來(lái)放本地排除網(wǎng)絡(luò)問(wèn)題console.log確認(rèn)registerMap確實(shí)執(zhí)行了、傳入的數(shù)據(jù)確實(shí)有features數(shù)組檢查容器寬高確認(rèn)chart.getWidth()不是0這四步能覆蓋95%的白屏問(wèn)題。5.3 Vite項(xiàng)目打包部署后地圖空白開(kāi)發(fā)環(huán)境地圖好好的打包上線之后白屏這是最讓人抓狂的問(wèn)題之一。定位下來(lái)大概率是public目錄下的靜態(tài)JSON文件路徑問(wèn)題。比如你把china.json放在了public/map/china.json開(kāi)發(fā)環(huán)境用/map/china.json能訪問(wèn)但項(xiàng)目部署在Nginx子路徑比如http://xxx.com/data-center/下/map/china.json就會(huì)解析成http://xxx.com/map/china.json404了。這個(gè)問(wèn)題的根本解法是用Vite的base配置并在代碼里使用相對(duì)路徑或import.meta.env.BASE_URL拼接// vite.config.js export default { base: process.env.NODE_ENV production ? /data-center/ : / }// 代碼里請(qǐng)求時(shí) const mapUrl ${import.meta.env.BASE_URL}map/china.json如果你用fetch請(qǐng)求相對(duì)路徑而不是絕對(duì)路徑也要注意路由History模式下路徑解析的問(wèn)題。最穩(wěn)妥的還是用import.meta.env.BASE_URL拼一遍。5.4 地圖縮放之后Tooltip錯(cuò)位roam: true開(kāi)啟后用戶拖拽或縮放地圖Tooltip偶爾會(huì)出現(xiàn)位置偏移。這個(gè)問(wèn)題的成因是在部分瀏覽器下ECharts的Tooltip定位依賴(lài)的是transform計(jì)算當(dāng)頁(yè)面有CSS動(dòng)畫(huà)或GPU加速層時(shí)會(huì)出現(xiàn)偏移。我的解法是給Tooltip的position指定一個(gè)回調(diào)函數(shù)tooltip: { position: function(point) { return [point[0] 20, point[1] 20] } }讓Tooltip相對(duì)于鼠標(biāo)位置偏移20像素基本能規(guī)避掉錯(cuò)位問(wèn)題。如果還是錯(cuò)位檢查一下頁(yè)面有沒(méi)有給Chart容器加transform: scale()之類(lèi)的縮放樣式有的話需要同步計(jì)算偏移量。6. 數(shù)據(jù)動(dòng)態(tài)更新與多種展示效果的進(jìn)階組合6.1 setOption的正確姿勢(shì)不要重復(fù)初始化大屏場(chǎng)景最常見(jiàn)的就是定時(shí)刷新數(shù)據(jù)。很多人一刷新就echarts.init一個(gè)新實(shí)例然后舊實(shí)例就泄漏了內(nèi)存暴漲頁(yè)面慢慢卡死。正確的做法是只init一次后面所有更新都通過(guò)setOption完成// 數(shù)據(jù)刷新時(shí) function updateMapData(newData) { chart.setOption({ series: [ { data: newData } ] }) }這里還有個(gè)小細(xì)節(jié)setOption默認(rèn)是合并模式也就是說(shuō)你傳入的配置會(huì)和之前的配置合并而不是完全替換。這在大屏場(chǎng)景下特別好用——更新數(shù)據(jù)時(shí)不用把tooltip、visualMap、series樣式全寫(xiě)一遍只傳要改的部分就行。但如果你要徹底重置圖表需要加第二個(gè)參數(shù)chart.setOption(option, true)表示不合并直接替換。6.2 地圖上疊加散點(diǎn)用effectScatter做呼吸燈效果中國(guó)地圖只顯示色塊總覺(jué)得不夠炫。最常見(jiàn)的增強(qiáng)是疊加城市散點(diǎn)尤其是有具體城市數(shù)據(jù)的時(shí)候。比如你有一批城市的數(shù)據(jù)要標(biāo)注用effectScatter系列可以做出光暈擴(kuò)散的呼吸燈效果{ series: [ { // 已有的地圖系列 type: map, map: china }, { name: 重點(diǎn)城市, type: effectScatter, coordinateSystem: geo, // 關(guān)鍵散點(diǎn)使用geo坐標(biāo)系 data: [ { name: 北京, value: [116.46, 39.92, 100] }, { name: 上海, value: [121.48, 31.22, 95] } ], symbolSize: function(val) { return Math.max(val[2] / 5, 8) }, rippleEffect: { brushType: stroke, scale: 4 } } ] }這里value數(shù)組的前兩位是經(jīng)緯度第三位是數(shù)值用來(lái)控制散點(diǎn)大小。這個(gè)系列的類(lèi)型是effectScatter坐標(biāo)系明確指到geo這樣散點(diǎn)才能定位到地圖上。正是因?yàn)橛羞@個(gè)方案關(guān)于geo和series.typemap的選擇題就有了標(biāo)準(zhǔn)答案如果只是想畫(huà)一張靜態(tài)色塊地圖用series.map就夠了如果想在地圖上方疊加其他層級(jí)的可視化元素散點(diǎn)、飛線、氣泡就用geo作為坐標(biāo)基底其他系列通過(guò)coordinateSystem: geo往上面疊。兩種方式可以共存用geo組件作為map的坐標(biāo)系背景同時(shí)再疊加散點(diǎn)層互不沖突。6.3 美好細(xì)節(jié)的打磨漸變、邊框與數(shù)據(jù)標(biāo)簽最后分享幾個(gè)提升地圖質(zhì)感的細(xì)節(jié)調(diào)整。地圖區(qū)域漸變效果ECharts支持areaColor配置漸變但地圖的gradient和普通圖形的漸變略有區(qū)別。在地圖系列中itemStyle不支持LinearGradient。所以如果你要做漸變效果建議用visualMap的inRange.color數(shù)組模擬漸變的視覺(jué)效果——多個(gè)顏色按數(shù)值過(guò)渡本身就像漸變。區(qū)域描邊設(shè)置itemStyle.borderColor和borderWidth可以讓省份邊界更清晰itemStyle: { borderColor: #fff, borderWidth: 1 }白色描邊在大屏深色背景下特別干凈。區(qū)域的hover效果默認(rèn)的emphasis效果是改變高亮的透明度或顏色。想要更細(xì)致的控制可以配置emphasis: { label: { show: true, color: #fff, fontSize: 14, fontWeight: bold }, itemStyle: { areaColor: #ffd666, shadowBlur: 10, shadowColor: rgba(0, 0, 0, 0.4) } }數(shù)據(jù)標(biāo)簽label.show設(shè)置為true時(shí)所有省份名都會(huì)顯示在地圖上。當(dāng)數(shù)據(jù)點(diǎn)密集的時(shí)候標(biāo)簽會(huì)互相重疊。解決辦法是開(kāi)啟label.layout的隱藏重疊策略或者給label加一個(gè)minMargin參數(shù)。實(shí)際項(xiàng)目中我一般用label: { show: true, fontSize: 10 }配layout: hideOverlap效果比較穩(wěn)。樣式這塊沒(méi)有絕對(duì)標(biāo)準(zhǔn)核心思路是根據(jù)大屏的背景色反推配色深色背景用亮色地圖淺色背景用飽和度低一點(diǎn)的色系保持視覺(jué)協(xié)調(diào)。做地圖這種可視化70%的時(shí)間其實(shí)都花在了數(shù)據(jù)準(zhǔn)備和排查問(wèn)題上真正寫(xiě)option的時(shí)間反而不多。把地圖數(shù)據(jù)獲取、registerMap、實(shí)例生命周期這幾塊基礎(chǔ)打扎實(shí)后面無(wú)論是做省份下鉆、城市散點(diǎn)還是飛線動(dòng)效都只是在這個(gè)框架上加料而已。如果你在做的過(guò)程中碰到什么奇怪的報(bào)錯(cuò)建議先確認(rèn)三件事數(shù)據(jù)源是不是好的、地圖有沒(méi)有注冊(cè)成功、實(shí)例是不是被響應(yīng)式污染了大部分問(wèn)題都能在這三個(gè)方向找到答案。