可視化大屏項(xiàng)目,該如何上手?(內(nèi)含源碼、示例)

起源

原本公司項(xiàng)目都是完成一些后臺(tái)管理的內(nèi)容還有移動(dòng)端的內(nèi)容,最近商務(wù)那邊對(duì)接了兩個(gè)關(guān)于可視化大屏的項(xiàng)目,雖說(shuō)公司前端在此類(lèi)項(xiàng)目上面沒(méi)有什么經(jīng)驗(yàn)積累,但畢竟沒(méi)吃過(guò)豬肉還是見(jiàn)過(guò)豬跑的,在著手開(kāi)發(fā)之前,需要先準(zhǔn)備一些解決通用問(wèn)題的開(kāi)發(fā)框架,方便后續(xù)相關(guān)大屏項(xiàng)目的快速迭代。

思考

關(guān)于可視化大屏項(xiàng)目的開(kāi)荒,我總結(jié)了以下幾個(gè)問(wèn)題

  1. 可視化圖表庫(kù)應(yīng)該如何選擇?
  2. 大屏開(kāi)發(fā)框架應(yīng)該如何搭建?
  3. 大屏基礎(chǔ)組件應(yīng)該如何設(shè)計(jì)與實(shí)現(xiàn)?
  4. 大屏渲染內(nèi)容圖表的定位問(wèn)題及縮放問(wèn)題如何解決?
  5. 大屏項(xiàng)目開(kāi)發(fā)代碼結(jié)構(gòu)該如何組織?

調(diào)研與落地

1、可視化圖標(biāo)庫(kù)應(yīng)該如何選擇?

鑒于可視化技術(shù)已經(jīng)相對(duì)成熟了,市面上的開(kāi)源可視化圖表庫(kù)也是繁華繚亂的,那我們應(yīng)該怎么選擇呢?

關(guān)于這個(gè)問(wèn)題,我個(gè)人也是查閱了很多資料,看了一些圖表庫(kù)的官方文檔及示例,結(jié)合一些內(nèi)容作者的分享成果作以下分析:

1)ECharts.js

官網(wǎng):https://echarts.apache.org

ECharts.js 最早是由百度技術(shù)團(tuán)隊(duì)維護(hù)的,
后來(lái)移交給了 Apache 開(kāi)源基金會(huì)孵化

也是我最終選擇的圖表庫(kù),選擇它的原因也是因?yàn)槲臋n界面相對(duì)友好,支持 SVGCanvas 雙擎渲染,圖表示例也比較全,而且文檔是支持中英文的,使用的人也比較多,所以相關(guān)資料也很豐富

這里要說(shuō)一點(diǎn)坑就是,文檔、示例雖好,但是很多特殊效果真的需要仔細(xì)閱讀示例 demo 和配置項(xiàng)文檔才能了解清楚,因?yàn)椋?code>ECharts 的配置選項(xiàng)實(shí)在是太龐大啦,對(duì)于第一次接觸的我來(lái)說(shuō),還是有點(diǎn)吃力,不過(guò)還好,套路摸清楚之后就可以很快復(fù)制

2)Chart.js

英文原版:https://www.chartjs.org

中文版:https://chartjs.bootcss.com

同樣作為一款文檔支持中英文的圖表庫(kù),我也是把它納入了對(duì)比范圍

文檔整體結(jié)構(gòu)和界面都是非常友好的,也擁有相當(dāng)數(shù)量的基礎(chǔ)內(nèi)置圖表,對(duì)于常規(guī)開(kāi)發(fā)來(lái)說(shuō),這個(gè)庫(kù)也是比較不錯(cuò)的選擇,Api 學(xué)習(xí)難度低于 ECharts

但是如果有復(fù)雜圖表比如儀表盤(pán)或者地圖相關(guān)渲染的時(shí)候,這個(gè)庫(kù)就不支持了,不過(guò)也不影響這個(gè)庫(kù)的好用,大家可以根據(jù)自己的業(yè)務(wù)需求來(lái)選擇,混合選擇多款圖表庫(kù)也是可行的

3) Antv

官網(wǎng):https://antv.vision/zh

國(guó)內(nèi)鏡像:https://antv.gitee.io/zh/

Antv 是我一開(kāi)始比較看好的一個(gè)可視化圖表庫(kù),它的產(chǎn)品系列劃分很多,根據(jù)不同的圖表類(lèi)型也分了很多不同的產(chǎn)品線(xiàn)

  • G2(可視化圖形)、G2Plot(通用圖表庫(kù))
  • S2(多維可視分析表格)
  • G6(關(guān)系數(shù)據(jù)圖分析工具)
  • X6(圖編輯引擎)
  • L7(地理空間數(shù)據(jù)可視化)
  • F2(專(zhuān)注移動(dòng)端的可視化解決方案)

文檔的質(zhì)量畢竟大廠(chǎng)的產(chǎn)品,還是非常能打的,目前是免費(fèi)使用的,但是后期是否會(huì)收費(fèi)就不清楚了,目前看這些項(xiàng)目在 GitHub 都是開(kāi)源的 MIT,如果有符合需求的也可以考慮。

但是也看到有人說(shuō)引入后本地可以運(yùn)行,但是部署在服務(wù)器發(fā)生了未知 bug,導(dǎo)致圖像無(wú)法渲染,所以也就沒(méi)有使用,畢竟沒(méi)有深入了解過(guò),或許這種問(wèn)題已經(jīng)修復(fù)了。

4) D3.js

官網(wǎng):https://d3js.org/

純英文文檔,相信這一條或許會(huì)勸退很多人,GitHub 有國(guó)內(nèi)開(kāi)發(fā)者翻譯的 Api 中文手冊(cè),沒(méi)有內(nèi)置圖表

但是其定義的繪圖開(kāi)發(fā)框架可以讓你用 Api 的方式來(lái)進(jìn)行 SVG 繪圖,這一點(diǎn)比使用原生 SVG 要好很多,如果大家有需求要進(jìn)行自定義繪制的,可以考慮使用 D3.js。

一句話(huà):很底層,但是足夠靈活,可滿(mǎn)足絕大部分圖表內(nèi)容的繪制

2、大屏開(kāi)發(fā)框架應(yīng)該如何搭建?

1)為什么要搭建框架?

如果我們直接上手畫(huà)圖的話(huà),的確很快,每種圖標(biāo)的配置都來(lái)一份,每個(gè)框框標(biāo)題都用 Div 一把梭,完全沒(méi)有心智負(fù)擔(dān)

但是來(lái)第二套、第三套、第四套圖的時(shí)候,是不是要吐血了。

拷代碼再改?如果兩期離得近還行,如果放個(gè)把月半年再來(lái),是不是忘得差不多了。。

如果 UI 視覺(jué)稿的風(fēng)格基本一致的情況下,是不是有一套可視化開(kāi)發(fā)框架會(huì)爽很多?

2)如何搭建框架呢?

這個(gè)問(wèn)題,沒(méi)有標(biāo)準(zhǔn)答案,我個(gè)人的理解,按需求和設(shè)計(jì)稿以及你所選擇的圖表庫(kù)共同決定的。

理論上,好的框架設(shè)計(jì)應(yīng)該是支持底層圖表庫(kù)替換而應(yīng)用層無(wú)需修改業(yè)務(wù)代碼的,所以 Api 的設(shè)計(jì)就尤為重要了。

那我們理一理,開(kāi)發(fā)一款大屏項(xiàng)目,需要經(jīng)歷那些設(shè)計(jì)?

大屏幕、大標(biāo)題、每個(gè)圖表的邊框、每個(gè)圖表的文字說(shuō)明、繪圖引擎、屏幕縮放的時(shí)候畫(huà)面跟著等比縮放等等

那我們就應(yīng)該考慮對(duì)上述問(wèn)題有一個(gè)統(tǒng)一的解決方案,這樣后續(xù)在進(jìn)行大屏項(xiàng)目開(kāi)發(fā)時(shí)就能夠事半功倍了。

3、大屏基礎(chǔ)組件應(yīng)該如何設(shè)計(jì)與實(shí)現(xiàn)?

我對(duì)我們現(xiàn)有的可視化大屏項(xiàng)目做了這樣幾個(gè)拆分

  • Screen - 大屏
  • Screen Title - 大屏標(biāo)題
  • Card - 數(shù)據(jù)內(nèi)容卡片
  • Card Title - 數(shù)據(jù)卡片標(biāo)題
  • Swiper - 大屏內(nèi)容滾動(dòng)
  • Dance Number - 跳動(dòng)的數(shù)字
  • ECharts - 圖表

通過(guò)這樣拆解,將 UI 視覺(jué)稿的內(nèi)容細(xì)分為這幾大模塊并分別管理和實(shí)現(xiàn),雖然不多,但足以應(yīng)對(duì)目前的需求內(nèi)容了,靈活度也足夠,可以隨視覺(jué)稿調(diào)整隨時(shí)替換。

除過(guò) ECharts 的部分都非常的簡(jiǎn)單易懂,做常規(guī) UI 組件分離即可,但是 ECharts 組件,到底該怎么設(shè)計(jì)呢?

用過(guò) ECharts 的小伙伴肯定知道,每一個(gè)圖表都需要單獨(dú)實(shí)例化一個(gè)對(duì)象用來(lái)作為圖表管理和繪制,而大屏項(xiàng)目往往會(huì)有很多個(gè)這樣的圖表,所以用組件的方式來(lái)自動(dòng)生成和管理這樣的 echarts 對(duì)象,我們只需要通過(guò)傳入 options 即可完成圖表的繪制。

其次,我們還可以通過(guò)組件來(lái)統(tǒng)一管理當(dāng)屏幕變化時(shí)造成的圖表 resize 的情況,封裝了組件之后,都可以在組件內(nèi)部進(jìn)行統(tǒng)一的事件監(jiān)聽(tīng)

乍一看,好像組件內(nèi)部也沒(méi)什么東西,事實(shí)上,就是這些東西就夠了。

因?yàn)?ECharts 的核心內(nèi)容,還是在于其龐大的 Options 對(duì)象。

如何對(duì) ECharts 的 Options 進(jìn)行抽象?

常見(jiàn)的圖表有柱狀圖、折線(xiàn)圖、面積圖、餅圖、環(huán)形圖

我們要做的就是按圖的類(lèi)型、坐標(biāo)軸、顏色這幾個(gè)維度來(lái)進(jìn)行考慮

我依據(jù)設(shè)計(jì)稿的風(fēng)格,對(duì) xAxis、yAxis、color 這三個(gè)較為通用的配置進(jìn)行了統(tǒng)一管理,設(shè)置一套默認(rèn)的配置,用于支持常規(guī)的柱狀圖及折線(xiàn)圖

再根據(jù) Options 的使用習(xí)慣,按照?qǐng)D表基本配置和數(shù)據(jù)源配置做工廠(chǎng)方法動(dòng)態(tài)生成

大概是這個(gè)樣子:

/**
 * 快捷創(chuàng)建柱狀圖配置,需自行根據(jù)需求設(shè)置 category
 * @param colors 普通顏色或漸變色
 * @param categorys 坐標(biāo)軸的類(lèi)別數(shù)據(jù)源 可選
 * @param direction 垂直或水平 默認(rèn) 垂直
 * @returns
 */
function createBarOpts(colors: BBColors[], categorys?: string[], direction = BarDirectionEnum.Vertical): any

/**
 * 快捷創(chuàng)建 BarSeriesItem 對(duì)象
 * @param values 數(shù)據(jù)源 可選
 * @param direction 垂直或水平 默認(rèn) 垂直
 * @returns
 */
function createBarSeriesItem(values?: any[], direction = BarDirectionEnum.Vertical): any

同樣的,相同的思想我們可以用統(tǒng)一的 Api 風(fēng)格擴(kuò)展為 createLineOpts、createLineSeriesItem、createPieOpts、createPieSeriesItem 等等

封裝好之后的實(shí)際開(kāi)發(fā)場(chǎng)景代碼就會(huì)變成這樣:

const opts = coumputed(() => {
    const categorys: string[] = [];
    const values: string[] = [];
    // 處理接口數(shù)據(jù)
    props.itemList?.forEach(item => {
        categorys.push(item.dateFormat);
        values.push(item.count);
    })
    // 生成基本 bar 配置
    const barOpts = createBarOpts([createGradientColors(["#E6AE28", "#FF8A00"])], categorys);
    // 生成基本 barSeriesItem 配置
    barOpts.series = [createBarSeriesItem(values)];
    // 雙柱狀圖可以這么寫(xiě)
    // barOpts.series = [createBarSeriesItem(values), createBarSeriesItem(values2)];
    
    return barOpts;
})

一下子節(jié)省了好多代碼有木有,業(yè)務(wù)上邏輯就會(huì)清晰不少,當(dāng)然,barOpts 實(shí)際上也是一個(gè)常規(guī)的對(duì)象,如果有定制的修改配置的情況,也是完全支持夠用的

4. 大屏渲染內(nèi)容圖表的定位問(wèn)題及縮放問(wèn)題如何解決?

查了很多資料,大致有這么幾種解決方案:

  • rem
  • vw
  • scale 等比縮放

相對(duì)于 rem 方案來(lái)說(shuō),個(gè)人更傾向于 vw 方案,畢竟 rem 橫豎屏切換的時(shí)候會(huì)有問(wèn)題(需要監(jiān)聽(tīng)屏幕變化重新設(shè)置 rootFontSize),需要刷新過(guò)才可以,vw 就沒(méi)有這個(gè)擔(dān)憂(yōu)。transformscale 縮放也是很好用的,但是縮放畢竟存在字體可能模糊的情況(某些極端屏幕尺寸下才存在)

這里我就簡(jiǎn)單介紹一下 vw 的適配方案:

1. 安裝 postcss-px-to-viewport 插件

pnpm add postcss-px-to-viewport -D

2. 配置 postcss-px-to-viewport config

import pxtovw from "postcss-px-to-viewport";
// postcss-px-to-viewport config
const pxtovw_config = pxtovw({
    unitToConvert: "px", // 要轉(zhuǎn)化的單位
    viewportWidth: 1920, // UI設(shè)計(jì)稿的寬度
    unitPrecision: 6, // 轉(zhuǎn)換后的精度,即小數(shù)點(diǎn)位數(shù)
    propList: ["*"], // 指定轉(zhuǎn)換的css屬性的單位,*代表全部css屬性的單位都進(jìn)行轉(zhuǎn)換
    viewportUnit: "vw", // 指定需要轉(zhuǎn)換成的視窗單位,默認(rèn)vw
    fontViewportUnit: "vw", // 指定字體需要轉(zhuǎn)換成的視窗單位,默認(rèn)vw
    selectorBlackList: ["ignore-"], // 指定不轉(zhuǎn)換為視窗單位的類(lèi)名,
    minPixelValue: 1, // 默認(rèn)值1,小于或等于1px則不進(jìn)行轉(zhuǎn)換
    mediaQuery: false, // 是否在媒體查詢(xún)的css代碼中也進(jìn)行轉(zhuǎn)換,默認(rèn)false
    replace: true, // 是否轉(zhuǎn)換后直接更換屬性值
});

3. 代碼中可以隨意使用 px 會(huì)自動(dòng)轉(zhuǎn)換為 vw

vw 也存在弊端,比如如果有一些樣式是通過(guò) ts 設(shè)置的,就需要自己實(shí)現(xiàn)一個(gè) px2vw 的方法進(jìn)行轉(zhuǎn)化。

export function px2vw(px: number, root: number = 1920, fixed = 6) {
    const res = (px / root) * 100;
    return `${res.toFixed(fixed)}vw`;
}

4、全局監(jiān)聽(tīng) window.resize 事件

這里需要注意的時(shí)候,建立一個(gè)消息中心,通過(guò)事件發(fā)布的方式通知每個(gè)圖表進(jìn)行自身的 resize,建議 window.resizethrottle 處理,提高性能。

5. 大屏項(xiàng)目開(kāi)發(fā)代碼結(jié)構(gòu)該如何組織?

前面框架已經(jīng)搭建的七七八八了,現(xiàn)在到了最重要的開(kāi)發(fā)環(huán)節(jié),那么項(xiàng)目代碼如何組織會(huì)更加利于后期的閱讀和維護(hù)呢?

這里我談一下我自己的思考:

  • 希望可以通過(guò)代碼結(jié)構(gòu)迅速了解到每個(gè)圖表模塊的相對(duì)位置
  • 快速調(diào)整大屏圖表布局結(jié)構(gòu)及尺寸

由于我們是 vw 的響應(yīng)式設(shè)計(jì),所以在進(jìn)行布局的時(shí)候就嚴(yán)格按照設(shè)計(jì)稿的尺寸進(jìn)行每個(gè)圖表塊的寬高進(jìn)行設(shè)置。

布局策略我用了較為簡(jiǎn)單的 Flex 布局,上面我們也提到了 Card 組件,Card 組件我設(shè)計(jì)了寬高屬性,這樣通過(guò) Flex + Weight、Height 的設(shè)置就可以很清楚的表達(dá)整個(gè)大屏子模塊的相對(duì)位置及尺寸

圖表的繪制由單獨(dú)的業(yè)務(wù)組件進(jìn)行維護(hù),這樣整個(gè) Index 文件就只存在 Card 布局結(jié)果及業(yè)務(wù)組件,沒(méi)有其他多余的部分,也方便我們后續(xù)的迭代開(kāi)發(fā)。

總結(jié)

第一次做可視化大屏項(xiàng)目,中間也是遇到過(guò)不少問(wèn)題,把第一個(gè)項(xiàng)目及框架搭建的過(guò)程整理記錄下來(lái),希望對(duì)自己也對(duì)能看到這篇文章的小伙伴有一點(diǎn)幫助。

預(yù)覽地址:ztstory.github.io/vue-datav

demo源碼地址:https://github.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容