動態(tài)刻度可視化組件實現
前言
之前有個網友問我了一個很有價值的問題, 有關實現數據可視化的問題, 但是這個可視化問題不是一般的柱狀圖, 折現圖之類的,而是不規(guī)則刻度的數據可視化.所以筆者思考了一下決定自己實現一個動態(tài)刻度可視化組件的方案, 來解決這一類的需求.
正文
最初的需求是這樣的:
我們只需要輸入文字, 數值比例, 就能生成如上圖所示的刻度圖.但是作為一名有追求的程序員, 需要對問題抽象化, 形成通用的解決方案,所以我們開始重組需求:
由上圖我們可以拆解為一下幾個需求點:
- 支持數值自定義
- 數值單位自定義
- 支持刻度組件寬度自定義
- 支持刻度線數量自定義
- 支持刻度變化幅度自定義
- 傳入已有進度比例,即激活區(qū)范圍
- 支持刻度樣式自定義
- 支持數值樣式自定義
- 支持自定義說明文本以及說明文本自定義
以上就是筆者挖掘的通用需求,當然有其他需求也可以漸進的增加.
確認了以上需求之后,我們開始選擇技術選型, 筆者之前常用的技術棧是vue和react,所以接下來我們初步確認該組件采用如下技術方案:
- react + typescript + umi-library
如果大家擅長使用vue, 也可以, 筆者之前也寫過如何搭建vue的組件庫相關的文章,感興趣可以學習了解一下, 其本質思想是一致的.
接下來我們開始實現動態(tài)刻度可視化組件. 如果對umi不熟悉的,可以參考筆者之前寫的文章從0到1教你搭建前端團隊的組件系統(tǒng)(高級進階必備).
1. 定義基本屬性類型
由以上需求分析我們可以定義如下的屬性類型:
- export interface TickerProps {
- width: number;
- maxHeight: number;
- percent: number;
- text: string;
- value: number;
- showValue: boolean;
- unit: string;
- lineNum: number;
- defaultColor: string;
- activeColor: string;
- textStyle: object;
- valueStyle: object;
- }
2. 組件整體結構
- const Ticker: React.FC<TickerProps> = function(props:TickerProps) {
- const {
- width = 100,
- maxHeight = 10,
- percent = 50,
- value,
- text = '瞬時能見度',
- showValue = true,
- unit = 'M',
- lineNum = 12,
- defaultColor = '#06c',
- activeColor = 'red',
- valueStyle,
- textStyle
- } = props
- return (
- <div className="ticker">
- {
- showValue &&
- <div className="value" style={valueStyle}>
- { value || 0 } <span className="unit">{ unit }</span>
- </div>
- }
- <div className="tickerGraph">
- <div className="tickerLine">
- </div>
- <div className="tickerBar"></div>
- </div>
- {
- !!text && <div className="text">{ text }</div>
- }
- </div>
- );
- };
- export default Ticker;
3. 視圖搭建
有關刻度可視化我們完全采用dom實現, 所以這里筆者具體分析一下如何實現刻度視圖:
上圖的思路就是用一個個dom來組裝成隨機刻度圖形,所以我們只要利用css實現這個形狀, 也就成功了一半.至于激活狀態(tài),我們會根據傳入的數據量來決定激活范圍,接下來會介紹如何渲染激活的刻度,也就是上圖的紅色區(qū)域.
4.特殊功能實現
因為該組件很多功能在搭建結構之后已經實現了, 這里我們唯一關注的就是css和js長度計算的問題, css實現方案有很多, 這里就不具體介紹了, 筆者這里重點介紹一下如何實現指定范圍的隨機高度:
- // 生成指定范圍的隨機高度
- const random = (min:number, max:number):number => {
- return min + Math.random() * (max - min)
- }
動態(tài)刻度條的隨機高度我們就是利用以上函數實現的, 刻度條內部實現如下:
- <div className="tickerLine" style={{borderBottomColor: defaultColor}}>
- {
- new Array(lineNum).fill(0).map((item:number, i: number) => {
- let isActive = (i + 1) <= Math.floor(lineNum * percent / 100)
- return <span
- className="tick"
- style={{
- height: random(3, maxHeight) + 'px', left: (gap + 2) * i + 'px',
- backgroundColor: isActive ? activeColor : defaultColor
- }}>
- </span>
- })
- }
- </div>
gap為刻度之間的間距, 由于計算刻度的位置需要一點幾何知識, 公式如下:
- W = Lw * lineNum + gap * ( lineNum - 1)
其中W表示刻度總寬度, Lw為刻度線寬度, lineNum為刻度線數量.
還有一個注意點就是激活態(tài), 筆者使用如下函數來判斷刻度是否具有激活狀態(tài):
- let isActive = (i + 1) <= Math.floor(lineNum * percent / 100)
這塊也非常好理解, 也就是我們傳入的比率乘以線的總數量,即可求出哪些刻度線是需要激活的.
以上細節(jié)實現完成之后,我們就可以來實現有點意思的刻度可視化方案啦, 如下展示的demo:
1.可見度測量
2.正態(tài)分布模型
3.標尺
4.光柵
5.自定義文本樣式
本文轉載自微信公眾號「趣談前端」
【編輯推薦】