Highcharts?圖表中圖例顯示狀態(tài)存儲(chǔ)的功能設(shè)計(jì)詳解
需求背景
公司前端使用 Highcharts 構(gòu)建圖表,圖表的圖例支持點(diǎn)擊顯示或隱藏相應(yīng)的指標(biāo)。現(xiàn)在有需求后端需要存儲(chǔ)用戶在前端點(diǎn)擊后顯示圖表的狀態(tài),對(duì)于已經(jīng)隱藏的圖例相應(yīng)的指標(biāo)線下次進(jìn)入頁(yè)面后依然隱藏。
這樣做的目的是簡(jiǎn)化圖表信息和去除噪音,有些信息對(duì)于某些用戶來(lái)說(shuō)是不太關(guān)心的,用戶取消顯示它們是合理的。
需求方案
方案一
使用數(shù)據(jù)庫(kù)枚舉值,將圖表的存儲(chǔ)狀態(tài)枚舉。這是一種實(shí)現(xiàn)上比較簡(jiǎn)單的方案,但是其組合較多,圖例越多所需要添加的枚舉值就越多,原本的圖表狀態(tài)中有兩個(gè)圖例可以進(jìn)行此操作,因此使用了這種方案。
現(xiàn)在需要將這個(gè)功能擴(kuò)展到 4 個(gè)圖表,未來(lái)又可能擴(kuò)展到更多圖表,所以這種簡(jiǎn)單的方案已經(jīng)不適用了。
方案二
使用一個(gè)數(shù)據(jù)庫(kù) JSON 類型字段存儲(chǔ)圖例的是否顯示的狀態(tài),其結(jié)構(gòu)類似下面這樣:
{ "chart_1":{ "legend_1":true, "legend_2":true, "legend_3":true, "legend_4":true, "legend_5":true, "legend_6":true, "legend_7":false, "legend_8":false }, "chart_2":{ "legend_1":true, "legend_2":true, "legend_3":true, "legend_4":true, "legend_5":true }, "chart_3":{ "legend_1":true, "legend_2":true, "legend_3":true, "legend_4":true }, "chart_3":{ "legend_1":true, "legend_2":true, "legend_3":true, "legend_4":true, "legend_5":true, "legend_6":true, "legend_7":true } }
這樣使用一個(gè)字段將所有圖表的圖例狀態(tài)都在一個(gè)地方維護(hù),而且避免了過(guò)多枚舉值的問(wèn)題。但在實(shí)際項(xiàng)目中因?yàn)槊總€(gè)用戶都有這樣一個(gè)值需要在數(shù)據(jù)庫(kù)中維護(hù),所以它會(huì)占用大量的數(shù)據(jù)庫(kù)存儲(chǔ)空間,在工程質(zhì)量和未來(lái)成本控制上與工程目標(biāo)不符。
方案三
對(duì)方案二進(jìn)行改進(jìn),存儲(chǔ)給每個(gè)圖表存儲(chǔ)一個(gè) int 值,實(shí)際這個(gè) int 值可以在代碼中轉(zhuǎn)換成二進(jìn)制,用每個(gè)二進(jìn)制值位的 0 和 1 來(lái)標(biāo)示圖例是否該顯示。其存儲(chǔ)結(jié)構(gòu)如下:
{ "chart_1": 15, "chart_2": 31, "chart_3": 127, "chart_3": 252 }
同時(shí)對(duì)于大部分用戶而言很可能是不會(huì)對(duì)默認(rèn)圖表做出更改,那么我們可以在代碼中維護(hù)一個(gè)默認(rèn)值,當(dāng)數(shù)據(jù)庫(kù)中相應(yīng)值與默認(rèn)值一致時(shí),無(wú)需實(shí)際存儲(chǔ)到數(shù)據(jù)庫(kù)中,在用戶獲取信息時(shí)直接返回默認(rèn)值即可。
代碼實(shí)現(xiàn)
后端使用的是 PHP 的 Laravel 框架,首先在相應(yīng)的 Model 里我新增了一些類似下面的常量:
public const BITMAP_INDEXES_BY_LEGEND_IDENTIFIER_BY_CHART_NAME = [ 'chart_1' => [ 'legend_1' => 7, 'legend_2' => 6, 'legend_3' => 5, 'legend_4' => 4, 'legend_5' => 3, 'legend_6' => 2, 'legend_7' => 1, 'legend_8' => 0, ], 'chart_2' => [ 'legend_1' => 3, 'legend_2' => 2, 'legend_3' => 1, 'legend_4' => 0, ], 'chart_3' => [ 'legend_1' => 6, 'legend_2' => 5, 'legend_3' => 4, 'legend_4' => 3, 'legend_5' => 2, 'legend_6' => 1, 'legend_7' => 0, ], 'chart_4' => [ 'legend_1' => 4, 'legend_2' => 3, 'legend_3' => 2, 'legend_4' => 1, 'legend_5' => 0, ], ]; // Each bit of a bitmap is like a bucket, which stores the displayed or hidden state. public const DEFAULT_CHART_LEGEND_VISIBILITIES = [ 'chart_1' => 0b1111, 'chart_2' => 0b11111, 'chart_3' => 0b1111111, 'chart_4' => 0b11111100, ];
BITMAP_INDEXES_BY_LEGEND_IDENTIFIER_BY_CHART_NAME 維護(hù)的是每個(gè)圖例在 bitmap 中的存儲(chǔ)位置,這樣在圖例增加時(shí)只需維護(hù)相應(yīng)的 chart 的存儲(chǔ)位置即可,不會(huì)影響到其他圖表的圖例存儲(chǔ)。
DEFAULT_CHART_LEGEND_VISIBILITIES 定義了圖例的默認(rèn)值,當(dāng)用戶未做更改時(shí)可以直接返回其值,在存儲(chǔ)時(shí)可進(jìn)行比對(duì),如果值一致就不存儲(chǔ)到數(shù)據(jù)庫(kù)里了。
在定義完這兩個(gè)常量后,需要做的就是對(duì)存儲(chǔ)和獲取的過(guò)程給予相應(yīng)的處理,Laravel 的 Model 提供了訪問(wèn)器和設(shè)置器的功能,我可以利用這個(gè)機(jī)制對(duì)值進(jìn)行處理,因此我在 model 里新增了以下方法:
/** * @return array<string,array<string,bool>> */ public function getChartLegendVisibilitiesAttribute(?string $value): array { $value = $value !== null ? \Safe\json_decode($value, true) : []; $value += self::DEFAULT_CHART_LEGEND_VISIBILITIES; $chart_legend_visibilities = []; /** @var string $chart_name */ foreach ($value as $chart_name => $legend_visibilities_bitmap) { foreach (self::BITMAP_INDEXES_BY_LEGEND_IDENTIFIER_BY_CHART_NAME[$chart_name] as $legend_identifier => $bitmap_index) { $chart_legend_visibilities[$chart_name][$legend_identifier] = (bool) (($legend_visibilities_bitmap >> $bitmap_index) & 1); } } ksort($chart_legend_visibilities); return $chart_legend_visibilities; } /** * @param array<string,array<string,bool>>|null $value * * @return void */ public function setChartLegendVisibilitiesAttribute(?array $value): void { if ($value !== null) { $chart_legend_visibilities = []; foreach ($value as $chart_name => $legend_visibilities) { $legend_visibilities_bitmap = 0; foreach ($legend_visibilities as $legend_identifier => $legend_visibility) { $bitmap_index = self::BITMAP_INDEXES_BY_LEGEND_IDENTIFIER_BY_CHART_NAME[$chart_name][$legend_identifier]; $legend_visibilities_bitmap |= ($legend_visibility & true) << $bitmap_index; } if ($legend_visibilities_bitmap !== self::DEFAULT_CHART_LEGEND_VISIBILITIES[$chart_name]) { $chart_legend_visibilities[$chart_name] = $legend_visibilities_bitmap; } } $value = $chart_legend_visibilities === [] ? null : \Safe\json_encode($chart_legend_visibilities); } $this->attributes[self::COLUMN_CHART_LEGEND_VISIBILITIES] = $value; }
這樣我所要實(shí)現(xiàn)的功能基本在這里完成了?,F(xiàn)在所要做的就是在控制器里對(duì)返回的圖表數(shù)據(jù)的狀態(tài)進(jìn)行綁定:
/** * @param array<string,array<int|string,mixed>> $chart_data * @param \App\Models\UserExtra $user_extra * @param string $chart_name * * @return array<string,array<int|string,mixed>> */ private static function getChartLegendVisibility(array $chart_data, UserExtra $user_extra, string $chart_name): array { $legend_label_id_by_store_index = array_flip(UserExtra::BITMAP_INDEXES_BY_LEGEND_IDENTIFIER_BY_CHART_NAME[$chart_name]); $legend_visibilities = $user_extra->chart_legend_visibilities[$chart_name]; foreach ($chart_data['series'] as $series_index => $series) { $key = array_shift($legend_label_id_by_store_index); $chart_data['series'][$series_index]['id'] = $key; $chart_data['series'][$series_index]['visible'] = $legend_visibilities[$key]; } return $chart_data; }
$chart_data 傳遞的是構(gòu)建好的 Highcharts 需要的數(shù)據(jù),這部分可以參考 Highcharts 的文檔。這個(gè)函數(shù)的目的就是將每個(gè)圖例的數(shù)據(jù)與我們所維護(hù)的圖例是否顯示的數(shù)據(jù)綁定,之后再提供修改圖例顯示狀態(tài)的 API 即可實(shí)現(xiàn)這個(gè)功能。
以上就是Highcharts 圖表中圖例顯示狀態(tài)存儲(chǔ)的功能設(shè)計(jì)詳解的詳細(xì)內(nèi)容,更多關(guān)于Highcharts 顯示狀態(tài)存儲(chǔ)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
php數(shù)組函數(shù)序列之a(chǎn)rray_intersect() 返回兩個(gè)或多個(gè)數(shù)組的交集數(shù)組
array_intersect() 函數(shù)返回兩個(gè)或多個(gè)數(shù)組的交集數(shù)組。結(jié)果數(shù)組包含了所有在被比較數(shù)組中,也同時(shí)出現(xiàn)在所有其他參數(shù)數(shù)組中的值,鍵名保留不變。2011-11-11《Head First 設(shè)計(jì)模式》代碼之PHP版(面向?qū)ο髮W(xué)習(xí))
《Head First 設(shè)計(jì)模式》是本不錯(cuò)的講解設(shè)計(jì)模式的書(shū),不像F4寫(xiě)的那么枯燥,應(yīng)該算是比較容易理解的好書(shū)。2010-06-06php中使用parse_url()對(duì)網(wǎng)址進(jìn)行解析的實(shí)現(xiàn)代碼(parse_url詳解)
parse_url用來(lái)解析一個(gè) URL 并返回一個(gè)關(guān)聯(lián)數(shù)組,包含在 URL 中出現(xiàn)的各種組成部分2012-01-01一款簡(jiǎn)單實(shí)用的php操作mysql數(shù)據(jù)庫(kù)類
這篇文章主要介紹了一款簡(jiǎn)單實(shí)用的php操作mysql數(shù)據(jù)庫(kù)類,不但包含了php針對(duì)mysql數(shù)據(jù)庫(kù)的常見(jiàn)操作之外,還有針對(duì)危險(xiǎn)字符的過(guò)濾功能,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-12-12phpstorm最新激活碼分享親測(cè)phpstorm2021.3版可用
這篇文章主要分享了phpstorm最新激活碼親測(cè)phpstorm2021.3.1版可用,如果你的提示過(guò)期可以使用phpstorm永久激活補(bǔ)丁+激活碼需要的朋友可以參考下2020-11-11在Windows XP下安裝Apache+MySQL+PHP環(huán)境
這篇文章主要介紹了在Windows XP下安裝Apache+MySQL+PHP環(huán)境的要點(diǎn)分析,需要的朋友可以參考下2015-02-02php命名空間設(shè)計(jì)思想、用法與缺點(diǎn)分析
這篇文章主要介紹了php命名空間設(shè)計(jì)思想、用法與缺點(diǎn),結(jié)合實(shí)例形式分析了php命名空間的概念、用法及相關(guān)的操作注意事項(xiàng),并給出了使用建議,需要的朋友可以參考下2019-07-07php使用Jpgraph創(chuàng)建3D餅形圖效果示例
這篇文章主要介紹了php使用Jpgraph創(chuàng)建3D餅形圖效果,結(jié)合實(shí)例形式分析了php基于Jpgraph繪制圖形的原理、實(shí)現(xiàn)步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-02-02