戰(zhàn):HTML+CSS+JS+ECharts從適配到地圖3D全解析)
簡介面向數(shù)據(jù)可視化開發(fā)者和運(yùn)維監(jiān)控人員這是一套基于HTML、CSS、JS與ECharts技術(shù)棧的大屏可視化項(xiàng)目源碼覆蓋柱狀圖、折線圖、餅圖、熱力圖、地圖等常用圖表的真實(shí)呈現(xiàn)方式。資源壓縮包共1341個文件總大小63.77MB以HTML頁面、CSS樣式、JavaScript交互腳本、JSON與XML數(shù)據(jù)文件為核心集成Bootstrap、AdminLTE等成熟樣式框架配套大量PNG/GIF圖標(biāo)、背景圖片、地圖GeoJSON、SVG圖標(biāo)及字體文件目錄模塊劃分清晰可直接在本地或服務(wù)器上運(yùn)行。項(xiàng)目完整串聯(lián)大屏開發(fā)的版式設(shè)計(jì)、響應(yīng)式適配、數(shù)據(jù)請求、定時(shí)刷新和交互篩選等關(guān)鍵環(huán)節(jié)內(nèi)置多套可切換的主題樣式和業(yè)務(wù)示例能快速移植到政務(wù)、園區(qū)、交通、物流等可視化大屏場景中。對想快速搭建數(shù)據(jù)大屏、入門ECharts圖表配置或提升前端綜合技能的讀者這份案例集可以直接運(yùn)行、對照修改是理解大屏項(xiàng)目完整流程的實(shí)用參照。已有1672人學(xué)習(xí)下載適合用于畢業(yè)設(shè)計(jì)、公司匯報(bào)或項(xiàng)目原型參考。 第一次接手大屏可視化項(xiàng)目很多人會有一個錯覺這不就是把幾個ECharts圖表塞進(jìn)一個大頁面里真動手才發(fā)現(xiàn)大屏可視化要處理的問題比普通后臺頁面復(fù)雜得多——固定設(shè)計(jì)稿如何適配各種拼接屏、十來個模塊怎么排才不擠不亂、圖表定時(shí)刷新時(shí)怎么做到不閃屏不抖動。這個項(xiàng)目我用純HTMLCSSJSECharts做了一套完整的指揮調(diào)度可視化大屏沒有引入重量級框架也沒有用大屏廠家的現(xiàn)成方案運(yùn)行下來很穩(wěn)。下面把整體思路和關(guān)鍵細(xì)節(jié)完整復(fù)盤一遍覆蓋頁面骨架搭建、布局細(xì)節(jié)、ECharts常見配置、地圖和3D效果最后是一份排錯清單。適合第一次做可視化大屏或者想把ECharts用得更明白的同學(xué)參考。1. 大屏項(xiàng)目開場頁面結(jié)構(gòu)骨架與分辨率適配1.1 先搞清楚大屏和普通頁面的本質(zhì)區(qū)別普通后臺頁面是內(nèi)容優(yōu)先用戶可以上下滾動瀏覽器窗口多大內(nèi)容就展示多少頁面內(nèi)容的完整性比視覺美觀重要。大屏不一樣它是視覺優(yōu)先通常部署在LED拼接屏、超寬屏或者豎屏展示終端上一屏要把關(guān)鍵指標(biāo)全部呈現(xiàn)幾乎不允許滾動。所以大屏開發(fā)的第一步不是寫代碼而是確認(rèn)設(shè)計(jì)稿的基準(zhǔn)尺寸。大多數(shù)大屏設(shè)計(jì)稿是1920×1080也有2560×1440、3840×1080這類超寬比例。設(shè)計(jì)稿尺寸一旦定了后面所有布局和圖表尺寸都圍繞它做。1.2 scale縮放方案為什么成為首選大屏適配通常有三條路vw/vh方案、rem方案、scale整體縮放方案。用vw/vh做適配好處是頁面完全跟隨窗口變化但16:9的設(shè)計(jì)稿放到非16:9的屏幕上圖表會被拉伸變形圓角變橢圓文字比例也會失真。rem方案需要計(jì)算根字號遇到復(fù)雜圖表時(shí)依然有精度損失。最省心的是scale方案容器始終按照1920×1080像素布局最后整體用transform: scale等比縮放到窗口里內(nèi)部所有圖表的比例完全不變。核心代碼思路如下function resizeScreen() { const designW 1920; const designH 1080; const winW window.innerWidth; const winH window.innerHeight; const scaleX winW / designW; const scaleY winH / designH; const scale Math.min(scaleX, scaleY); document.getElementById(screen).style.transform scale( scale ); }注意transform-origin要設(shè)置成左上角(0 0)否則縮放中心是容器中心點(diǎn)定位會亂。還要給外層body設(shè)置overflow: hidden防止縮放后出現(xiàn)滾動條。如果屏幕邊緣出現(xiàn)1px左右的裁切或白邊可以在縮放比例后面乘一個1.01的保險(xiǎn)系數(shù)讓畫面稍微鋪出去一點(diǎn)。三種方案對比方案優(yōu)點(diǎn)缺點(diǎn)適用場景vw/vh全窗口自適應(yīng)非標(biāo)準(zhǔn)比例會拉伸變形寬高比固定的展示屏rem等比例文字圖表內(nèi)部細(xì)節(jié)精度有限偏內(nèi)容型的頁面scale等比縮放不動布局邊緣可能有黑邊或裁切固定設(shè)計(jì)稿的大屏項(xiàng)目1.3 大屏骨架的HTML結(jié)構(gòu)大屏的頁面結(jié)構(gòu)通常比較固定頂部是標(biāo)題欄中間主體分成左中右三塊左右一般是兩列或四列小面板中間放核心場景圖或者大地圖。下面是一個簡化版骨架!DOCTYPE html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title指揮調(diào)度可視化大屏/title style html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; } #screen { width: 1920px; height: 1080px; transform-origin: 0 0; background: linear-gradient(135deg, #0a1a3a 0%, #061024 100%); display: flex; flex-direction: column; } .header { height: 90px; flex: none; } .main { flex: 1; display: grid; grid-template-columns: 1fr 3fr 1fr; gap: 12px; padding: 0 12px 12px; min-height: 0; } /style /head body div idscreen div classheader標(biāo)題區(qū)域/div div classmain div classleft-panel左列/div div classcenter-panel中間主視圖/div div classright-panel右列/div /div /div script srcjs/echarts.min.js/script /body /html很多新人在這個階段最容易犯的錯是把大屏當(dāng)普通后臺頁面做上來就寫組件、寫路由結(jié)果最后展示時(shí)發(fā)現(xiàn)分辨率不對、布局錯位。先把根容器和縮放邏輯定好后面填圖表基本不會出大問題。2. 布局與視覺細(xì)節(jié)flex和grid怎么配合以及各種CSS裝飾效果2.1 宏觀柵格用grid內(nèi)部對齊用flex大屏的布局有兩個層面。宏觀層面幾大區(qū)塊之間的位置關(guān)系用grid最合適代碼語義清晰還能控制列寬比例。微觀層面每個面板內(nèi)部的標(biāo)題欄、指標(biāo)數(shù)字、工具按鈕用flex處理因?yàn)樗鼈冃枰鲚S向?qū)R。一個常見問題是flex: 1為什么最后還剩一點(diǎn)寬度。這個情況我在做一行三欄指標(biāo)卡時(shí)踩過很多次。原因是flex子項(xiàng)默認(rèn)的min-width是auto當(dāng)子項(xiàng)里面的內(nèi)容寬度超過flex分配的空間時(shí)子項(xiàng)不會縮小到分配寬度而是被內(nèi)容撐開導(dǎo)致最后一個子項(xiàng)比預(yù)期的窄右側(cè)就空出一段。解決辦法有三個給flex子項(xiàng)加min-width: 0或者用width: 0再配flex: 1或者全局加box-sizing: border-box確保padding不會額外撐開寬度。在大屏指標(biāo)卡里我習(xí)慣這組樣式.card { flex: 1; min-width: 0; box-sizing: border-box; padding: 12px; overflow: hidden; }順帶提醒如果卡片左側(cè)有圖標(biāo)、右側(cè)有單位文本內(nèi)部用flex justify-content: space-between比用float和padding硬懟穩(wěn)定得多。2.2 調(diào)整容器內(nèi)文本位置先分清行內(nèi)和塊級場景怎么調(diào)整css容器里的文本位置也是搜索高頻詞這個坑確實(shí)很典型。很多人一上來就用margin和padding拼位置結(jié)果換一個分辨率就偏移了。其實(shí)單行文字垂直居中最直接的是讓line-height等于容器高度多行文字垂直居中用flex的align-items: center如果既要水平居中又要垂直居中給容器加display: flex; align-items: center; justify-content: center。大屏場景還有一類特殊文本數(shù)字和單位混排。比如營業(yè)額 1,238.45 萬這種默認(rèn)文字基線對齊會讓數(shù)字和中文不在一條視覺水平線上。解決辦法是把數(shù)字和單位分別放在span里給數(shù)字設(shè)置vertical-align: baseline或者統(tǒng)一用flex布局把兩部分對齊。2.3 讓大屏有科技感的CSS裝飾成本比想象中低大屏的視覺風(fēng)格基本是深色底、發(fā)光邊框、動態(tài)流光、旋轉(zhuǎn)光環(huán)這一套。這些效果不需要圖片素材幾個CSS基礎(chǔ)語法就能實(shí)現(xiàn)。旋轉(zhuǎn)光環(huán)可以用一個帶漸變邊框的圓角容器加animation旋轉(zhuǎn).ring { width: 240px; height: 240px; border-radius: 50%; border: 2px solid rgba(0, 255, 200, 0.6); border-top-color: transparent; animation: spin 6s linear infinite; } keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }漣漪光圈擴(kuò)散也是大屏雷達(dá)圖、中心按鈕的常用裝飾本質(zhì)是透明圓環(huán)從1倍大小放大到2倍同時(shí)透明度降到0循環(huán)執(zhí)行。字體漸變這類效果用background-clip: text配合線性漸變就行注意同時(shí)設(shè)置color: transparent否則看不到漸變效果。免費(fèi)素材站方面CodePen和CodeMyUI上有大量現(xiàn)成的玻璃擬態(tài)卡片、動態(tài)邊框、粒子背景直接搜大屏“科技感”關(guān)鍵詞比自己從零寫快很多。3. ECharts核心配置與數(shù)據(jù)動態(tài)上屏3.1 引入方式正式大屏項(xiàng)目別只用CDNECharts的引入有兩種常見路徑。開發(fā)階段用CDN很方便一個script標(biāo)簽就能用。但大屏要部署在現(xiàn)場的服務(wù)器甚至可能是離線環(huán)境所以建議把echarts.min.js下載到本地放在項(xiàng)目js目錄下和頁面同級引用。這樣即使外部網(wǎng)絡(luò)斷開頁面照樣渲染。如果后面還要做3D地圖需要額外引入echarts-gl.min.js而且必須在echarts.min.js之后加載否則3D注冊不進(jìn)去會報(bào)series.map3D not exists這類錯誤。3.2 實(shí)例化與setOption的基礎(chǔ)邏輯每一個圖表都必須有一個明確寬高的DOM容器這一點(diǎn)很容易被忽略。很多大屏圖表白屏排查到最后發(fā)現(xiàn)是容器height: 0。初始化代碼本身非常簡單const chart echarts.init(document.getElementById(chart1)); chart.setOption({ tooltip: {}, series: [{ type: pie, data: [ ... ] }] });setOption是增強(qiáng)式配置重復(fù)調(diào)用時(shí)傳入的配置項(xiàng)會與之前的配置合并而不是整體覆蓋。這個特性在動態(tài)刷新時(shí)非常有用比如只更新series里的data其他配置項(xiàng)會保留動畫過渡也很平滑。每張大屏通常有十幾個圖表要給每個圖表實(shí)例用變量管理起來不要用完就丟。頁面關(guān)閉或重新加載時(shí)用chart.dispose()銷毀實(shí)例避免內(nèi)存占用。3.3 visualMap.pieces分段地圖和熱力圖的顏色映射利器在ECharts里做熱力圖或地圖數(shù)值映射最常用的是visualMap。項(xiàng)目里常遇到的需求是把這個指標(biāo)分成9段顏色改成10段怎么弄。這個完全不用改數(shù)據(jù)結(jié)構(gòu)只需要修改pieces數(shù)組的長度。visualMap: { type: piecewise, pieces: [ { min: 100, max: 500, label: 嚴(yán)重 }, { min: 50, max: 100, label: 較重 }, { min: 10, max: 50, label: 一般 }, { min: 0, max: 10, label: 正常 } ], inRange: { color: [#0b3a5e, #1d6f9c, #36b6a0, #e6b84c] } }把pieces數(shù)組加一段顏色數(shù)組也對應(yīng)加一個圖例分段數(shù)就變了。注意pieces里min和max不要寫重疊比如一段的max是100下一段的min就不能也是100否則數(shù)據(jù)歸屬會有歧義。color數(shù)組建議由深到淺或由淺到深遞減視覺上更容易讀懂。數(shù)據(jù)格式化方面大屏圖表里經(jīng)常要顯示1,238.45萬這種格式。不要直接在數(shù)值上拼接toFixed(2)要先做千分位分隔再拼單位function formatNum(num, unit) { const n Number(num).toFixed(2); const arr n.split(.); arr[0] arr[0].replace(/\B(?(\d{3})(?!\d))/g, ,); return arr.join(.) (unit || ); }3.4 定時(shí)刷新不閃屏setInterval的正確打開方式大屏需要實(shí)時(shí)數(shù)據(jù)最樸素的方案是setInterval輪詢每隔幾秒拉一次接口然后更新對應(yīng)圖表。關(guān)鍵點(diǎn)在于不要重建圖表實(shí)例只調(diào)用setOption更新data。這樣ECharts內(nèi)部的動畫系統(tǒng)會平滑過渡視覺上不會閃屏。另一個注意點(diǎn)是setInterval的周期不要小于1秒大屏上的數(shù)字跳動太快反而難以閱讀。業(yè)務(wù)數(shù)據(jù)2-3秒刷新一次比較合適地圖類數(shù)據(jù)5-10秒刷新一次。多個圖表共用一個定時(shí)器可以減少資源占用也可以在頁面隱藏時(shí)用document.hidden做節(jié)流后臺標(biāo)簽頁不再刷新。4. 地圖組件與三維可視化中國地圖、分段映射、3D圖表4.1 ECharts5里沒有內(nèi)置中國地圖數(shù)據(jù)必須注冊GeoJSONECharts從3代之后官方不再內(nèi)置中國地圖數(shù)據(jù)大屏里要用中國地圖第一步是自己準(zhǔn)備GeoJSON數(shù)據(jù)。給html引入GeoJSON文件有多種方式最簡單的是在script文件里定義一個全局變量然后調(diào)用echarts.registerMap(china, jsonData)注冊之后series里配置map: china就能使用。要注意的是地圖中每一塊區(qū)域的name必須和數(shù)據(jù)里的name一一對應(yīng)比如新疆內(nèi)蒙古寧夏這些都不能寫別名。如果對不上對應(yīng)的數(shù)值不會上色圖例看上去像缺了一塊。排查時(shí)先在console打印一遍注冊的map再對比series.data中的name基本就能定位。4.2 地圖9段變10段的具體操作前面visualMap.pieces部分已經(jīng)講過原理放到地圖上具體操作是保持series.map不變直接調(diào)整visualMap里的pieces數(shù)組。例如原來9個區(qū)間分別是0-100、100-200一直到800-900要變成10個區(qū)間就把最后一段拆成兩段同時(shí)inRange.color數(shù)組也補(bǔ)一個顏色。難點(diǎn)不在于代碼而在于區(qū)間怎么劃分才能讓顏色分布有區(qū)分度。建議先用真實(shí)數(shù)據(jù)的最大值做一次統(tǒng)計(jì)按分位數(shù)切分而不是憑感覺平均分。4.3 全球地圖顯示成長方形的坑ECharts的geo組件默認(rèn)是個矩形區(qū)域地圖數(shù)據(jù)本身是球形投影后的平面圖形如果坐標(biāo)系設(shè)置不對全球地圖會變成一張長方形里貼著幾個大陸碎片。解決辦法是給geo配置綁定好layoutCenter和layoutSize讓地圖在矩形區(qū)域內(nèi)自適應(yīng)居中g(shù)eo: { map: world, layoutCenter: [50%, 50%], layoutSize: 80% }如果是中國地圖同樣可以用layoutCenter把地圖固定在容器的某個位置而不是整個矩形鋪滿。遇到地圖組件長方形展示的問題先檢查是不是忘了配置layoutSize結(jié)果默認(rèn)把地圖鋪滿了geo容器。4.4 用echarts-gl做3D地圖geo3d、map3d、scatter3d怎么配合3D地圖是大屏里視覺沖擊力最強(qiáng)的內(nèi)容ECharts官方擴(kuò)展echarts-gl提供了map3d系列。引入echarts-gl.min.js后series配置type: map3d配合mapType指定注冊的地圖名稱再設(shè)置viewControl控制視角和自動旋轉(zhuǎn)series: [{ type: map3d, map: china, shading: color, data: [], viewControl: { autoRotate: true, distance: 100, alpha: 30, beta: 40 } }]scatter3d的常見用法是把業(yè)務(wù)數(shù)據(jù)以柱點(diǎn)的方式釘在地圖上方。它需要的是三維坐標(biāo)在ECharts里可以直接給data項(xiàng)配置lng、lat屬性ECharts會內(nèi)部轉(zhuǎn)換到地圖坐標(biāo)系。新手最容易踩的坑是忘了給scatter3d指定coordinateSystem: geo3d或map3d結(jié)果點(diǎn)全部畫在原點(diǎn)附近堆成一團(tuán)。3D效果好歸好但要控制場景復(fù)雜度。如果大屏上同時(shí)放三個3D地圖普通電腦的幀率會明顯下降。建議一個大屏最多放一個3D主場景其他模塊用2D圖表呼應(yīng)性能與視覺都能兼顧。5. 高頻問題排錯復(fù)盤從那些搜索關(guān)鍵詞里看真實(shí)痛點(diǎn)5.1 html文件無法預(yù)覽多半不是代碼問題大屏開發(fā)過程中很多人會直接雙擊打開html文件結(jié)果發(fā)現(xiàn)圖表不顯示、JS報(bào)錯于是反復(fù)改代碼也找不到原因。這個問題的根源不在代碼而在于file://協(xié)議的限制。瀏覽器直接打開本地文件時(shí)很多外部資源加載和接口請求都會被安全策略攔截。建議任何大屏項(xiàng)目都用本地靜態(tài)服務(wù)器起服務(wù)。最簡單的方式是在項(xiàng)目目錄執(zhí)行python -m http.server 8080或者用VS Code的Live Server插件之后訪問http://localhost:8080/index.html。這樣腳本、字體、接口請求都走h(yuǎn)ttp環(huán)境和最終部署行為基本一致。5.2 圖表白屏或只顯示一個灰塊圖表容器在flex布局時(shí)最容易遇到這種問題。flex子項(xiàng)里嵌套圖表組件如果子項(xiàng)沒有顯式設(shè)置高度圖表的canvas實(shí)際高度就是0。遇到白屏先右鍵檢查元素看容器尺寸是不是被壓縮了這是最快的排查路徑。還有一種情況是圖表先于頁面DOM渲染完成執(zhí)行script放在body最前面或者用了defer但沒有等DOMContentLoaded。把初始化邏輯放到window.onload里或者直接把script標(biāo)簽放在body末尾再初始化就不會有這個問題。5.3 大屏在高分屏上模糊發(fā)虛大屏部署的終端很多是高分屏或者Windows系統(tǒng)縮放比例不是100%。canvas會根據(jù)CSS尺寸繪制如果設(shè)備像素比高而canvas沒有開高分辨率文字和線條就會發(fā)虛。ECharts初始化時(shí)指定像素比即可const chart echarts.init(container, null, { devicePixelRatio: 2 });或者更通用一點(diǎn)在option配置里寫devicePixelRatio: window.devicePixelRatio。canvas按更高精度繪制畫面會銳利很多。5.4 動態(tài)刷新時(shí)動畫抖動和數(shù)據(jù)疊加動態(tài)刷新的閃屏問題前面提過用setOption的merge模式解決。但如果業(yè)務(wù)邏輯涉及完全替換數(shù)據(jù)結(jié)構(gòu)比如從展示今天換成展示昨天圖表類型可能都變了這時(shí)候要setOption的第二個參數(shù)傳true讓ECharts不做合并而是整體重置并且第三個參數(shù)也傳true強(qiáng)制立即更新避免出現(xiàn)舊數(shù)據(jù)殘留和新數(shù)據(jù)疊加的混亂。這個場景我在大屏切換日報(bào)/周報(bào)視圖時(shí)經(jīng)常遇到。如果只更新data不重置新舊數(shù)據(jù)會混在一起圖例也會變得很奇怪。完整跑完這套大屏項(xiàng)目后我的體會是大屏可視化不是圖表的堆砌而是先有穩(wěn)定的頁面骨架再有克制的視覺裝飾最后才是數(shù)據(jù)的正確表達(dá)。HTMLCSSJSECharts這套組合沒有框架負(fù)擔(dān)部署也簡單只要把適配方案、圖表生命周期和數(shù)據(jù)結(jié)構(gòu)這三件事想清楚完全能撐起一個正式項(xiàng)目的可視化場景。最后再分享一個小習(xí)慣每個圖表的配置單獨(dú)放在一個JS文件里管理不要全部堆在index.html否則后期改需求的時(shí)候光定位配置項(xiàng)就夠你翻半天。本文還有配套的精品資源點(diǎn)擊獲取