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

起源

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

思考

關(guān)于可視化大屏項目的開荒,我總結(jié)了以下幾個問題

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

調(diào)研與落地

1、可視化圖標庫應該如何選擇?

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

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

1)ECharts.js

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

ECharts.js 最早是由百度技術(shù)團隊維護的,
后來移交給了 Apache 開源基金會孵化

也是我最終選擇的圖表庫,選擇它的原因也是因為文檔界面相對友好,支持 SVG 、Canvas 雙擎渲染,圖表示例也比較全,而且文檔是支持中英文的,使用的人也比較多,所以相關(guān)資料也很豐富

這里要說一點坑就是,文檔、示例雖好,但是很多特殊效果真的需要仔細閱讀示例 demo 和配置項文檔才能了解清楚,因為,ECharts 的配置選項實在是太龐大啦,對于第一次接觸的我來說,還是有點吃力,不過還好,套路摸清楚之后就可以很快復制

2)Chart.js

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

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

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

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

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

3) Antv

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

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

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

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

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

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

4) D3.js

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

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

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

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

2、大屏開發(fā)框架應該如何搭建?

1)為什么要搭建框架?

如果我們直接上手畫圖的話,的確很快,每種圖標的配置都來一份,每個框框標題都用 Div 一把梭,完全沒有心智負擔

但是來第二套、第三套、第四套圖的時候,是不是要吐血了。

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

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

2)如何搭建框架呢?

這個問題,沒有標準答案,我個人的理解,按需求和設(shè)計稿以及你所選擇的圖表庫共同決定的。

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

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

大屏幕、大標題、每個圖表的邊框、每個圖表的文字說明、繪圖引擎、屏幕縮放的時候畫面跟著等比縮放等等

那我們就應該考慮對上述問題有一個統(tǒng)一的解決方案,這樣后續(xù)在進行大屏項目開發(fā)時就能夠事半功倍了。

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

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

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

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

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

用過 ECharts 的小伙伴肯定知道,每一個圖表都需要單獨實例化一個對象用來作為圖表管理和繪制,而大屏項目往往會有很多個這樣的圖表,所以用組件的方式來自動生成和管理這樣的 echarts 對象,我們只需要通過傳入 options 即可完成圖表的繪制。

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

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

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

如何對 ECharts 的 Options 進行抽象?

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

我們要做的就是按圖的類型、坐標軸、顏色這幾個維度來進行考慮

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

再根據(jù) Options 的使用習慣,按照圖表基本配置和數(shù)據(jù)源配置做工廠方法動態(tài)生成

大概是這個樣子:

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

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

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

封裝好之后的實際開發(fā)場景代碼就會變成這樣:

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)];
    // 雙柱狀圖可以這么寫
    // barOpts.series = [createBarSeriesItem(values), createBarSeriesItem(values2)];
    
    return barOpts;
})

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

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

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

  • rem
  • vw
  • scale 等比縮放

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

這里我就簡單介紹一下 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è)計稿的寬度
    unitPrecision: 6, // 轉(zhuǎn)換后的精度,即小數(shù)點位數(shù)
    propList: ["*"], // 指定轉(zhuǎn)換的css屬性的單位,*代表全部css屬性的單位都進行轉(zhuǎn)換
    viewportUnit: "vw", // 指定需要轉(zhuǎn)換成的視窗單位,默認vw
    fontViewportUnit: "vw", // 指定字體需要轉(zhuǎn)換成的視窗單位,默認vw
    selectorBlackList: ["ignore-"], // 指定不轉(zhuǎn)換為視窗單位的類名,
    minPixelValue: 1, // 默認值1,小于或等于1px則不進行轉(zhuǎn)換
    mediaQuery: false, // 是否在媒體查詢的css代碼中也進行轉(zhuǎn)換,默認false
    replace: true, // 是否轉(zhuǎn)換后直接更換屬性值
});

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

vw 也存在弊端,比如如果有一些樣式是通過 ts 設(shè)置的,就需要自己實現(xiàn)一個 px2vw 的方法進行轉(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)聽 window.resize 事件

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

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

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

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

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

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

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

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

總結(jié)

第一次做可視化大屏項目,中間也是遇到過不少問題,把第一個項目及框架搭建的過程整理記錄下來,希望對自己也對能看到這篇文章的小伙伴有一點幫助。

預覽地址:ztstory.github.io/vue-datav

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

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

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

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