最近項目的管理後臺都在用阿里粑粑開源的管理框架Ant Design Pro,說真話,仍是比較好用的。該框架內部也封裝了一些圖標插件,可是在最近的一個項目中發現,這些圖標可定製性仍是差了點,不能知足客戶需求。html
好在它的後面也介紹了本身親生的BizChart可視化圖表組件,由於定製性比較高,可是api中的介紹又不是每一個都有例子,更沒有組合使用的例子,通過度娘介紹,發現這片文章備受我青睞,我怕做者哪天不高興放棄了,因此轉存了一份,順便把本身實踐到的補充到後面。前端
做爲一個前端打字員,除了綠茶婊以外,最討厭的就是圖表:一個讓我傷心,一個讓我難過;比這更討厭的就是文檔寫得不清不楚的圖表庫(大機率是九年義務教育期間沒有學好語文),讓我又愛又恨!因此本篇博文會比較枯燥,只簡單描述一下使用BizCharts的過程,固然,重要的是總結遇到的坑(聽從一個坑不踩兩次,一個女生不泡兩次原則)。react
By the way,提到BizCharts,讓咱們感謝一下阿里巴巴:其開源了這個好用的(雖然偶爾不那麼好用,還偶得挺常常的)的react圖表庫供你們使用,對使用react技術棧的前端打字員來講簡直就是福音。本文不會有過多的api解釋,具體的接口能夠看官網文檔鬼門關。npm
剛開始,視覺設計師哄我說:「個人要求並不高,待我從前同樣好」,啊呸,說錯了,「個人要求是:只要能把數據用直方圖展示出來就行了」,so easyjson
// 引入相關的組件 import { Chart, Axis, Tooltip, Geom } from 'bizcharts'; // 隨便mock一下數據 const str = ['we', 'are', 'the', 'black', 'gold', 'team']; const mockData = () => { let result = []; for (let i = 0, len = 6; i < len; i++) { result.push({ xAxis: ticks[i], yAxis: Math.floor(Math.random() * 100) }); } return result; }; // 圖表組件 <Chart width={ 600 } height={ 400 } data={ mockData() } > {/* x軸,橫軸,以data數據的xAxis屬性值爲柱子的值 */} <Axis name="xAxis" /> {/* y軸,縱軸,以data數據的yAxis屬性值爲柱子的值 */} <Axis name="yAxis" /> {/* 鼠標hover直方圖柱子的時候,tooltip顯示的值 */} <Tooltip /> {/* 幾何標記對象,主要用以描述你要畫的是什麼圖形(直方圖、折線圖、餅狀圖、區域圖):interval是直方圖 */} <Geom type="interval" position="xAxis*yAxis" /> </Chart>
一波操做猛如狗,讓視覺設計師看看效果:api
note:此直方圖每一根柱子都是單點的,也就是其反映了某個橫座標點的數據狀況。加入咱們須要一個連續區間柱子,那麼做爲xAxis的數據字段值應該爲一個數組,包含兩個元素,代表區間的起始值。數組
const mockData = () => { let result = []; for (let i = 0, len = 6; i < len; i++) { result.push({ xAxis: [i + 0.01, i + 1 - 0.01], // 若是不加減0.01,那麼第一根柱子的終點跟第二根柱子的起點是同一個,會感受兩個柱子粘在一塊兒 yAxis: Math.floor(Math.random() * 100) }); } return result; };
視覺設計師:「emmmmmm,圖表長寬定死了會不會有點僵硬啊,高度能夠寫死,寬度總得來個自適應吧?」bash
「毛悶臺」antd
<Chart height={ 400 } data={ mockData() } forceFit // 我媽說了(guanfang wendang shuode),加上這個屬性就可使圖表寬度自適應了,隔壁echart同窗要學習一下 > </Chart>
效果秀一波app
視覺(蜜汁微笑):「猿子,你這玩意有bug啊」
「胡說,你這傻*不會用吧」
(義正詞嚴)「哼哼,放大窗口圖表寬度確實會自適應,可是縮小就掛掉了(並不自適應),ahhhhhh」
(諂媚)「討厭,再給我兩分鐘~~讓我把bug結成冰。。。」
note: 實驗代表,若是Chart組件的父組件Father採用flex佈局,即Father使用flex自適應寬度,那麼就會出現上述的問題;因此,若是有多個圖表同行並列佈局,請不要使用flex佈局,給Father組件的寬度設置爲百分比吧,此時的forceFit就會起做用了。同時,BizCharts對重繪設置了防抖,只有當中止縮放的時候纔會重繪。
(屌到飛起)「over,拿去用吧」
(一臉鄙視)「哇喔~好棒棒呀,敢不敢讓我調一點點小細節,我保證就一點點!」
「Come on baby!」
「
」
把視覺殺了以後,需求仍是要作的,先解決死者的第一個遺願。
Tooltip組件提供了一個屬性crosshairs,用以設置tooltip的輔助線和輔助框;默認狀況下,此屬性會爲’line’、‘area’、‘path’、‘areaStack’類型的Geom組件開啓垂直輔助線、爲‘interval’類型的Geom組件展現矩形背景框。死者說的很醜的方框就是這個!
<Tooltip crosshairs={ false }/>
好的,把框去掉了!咦,咱們不是說要修改它的顏色嗎?好的,改一下
<Tooltip crosshairs={{ type: "rect" // 可選值:rect、x、y、cross,分別對應輔助狂、平行x軸輔助線、平行y軸輔助線,十字輔助線 style: { fill: 'red', // 輔助框顏色 shadowColor: 'red', // 輔助框周邊陰影的顏色 shadowBlur: 1, // 輔助框周邊陰影的透明度 opacity: 0 // 輔助框的透明度 } }} />
note:假如開啓的是輔助線,即type不是「rect」,那麼上述的樣式定義將不起做用。究其緣由,看了此組件的源碼以後才發現,描述輔助線樣式的屬性不是style對象,而是lineStyle對象,官方文檔並未說明這一點。
<Tooltip crosshairs={{ type: "y" lineStyle: { stroke: 'red', // 輔助線顏色 lineWidth: 4, // 輔助線寬度,單位爲px opacity: 1 // 輔助線透明度 } }} />
看起來仍是很容易就實現了死者的第一個遺願,就這樣懟死了視覺,是否是太殘忍了點?事已至此,繼續實現他的遺願吧。
第二個遺願是給tooltip換個樣式。既然要修改tooltip的樣式,就應該繼續對Tooltip組件下手。經過閱讀文檔,發現其還有一個itemTpl的屬性,也就是能夠經過這個屬性定義tooltip的模板
// 定義一個模板 // name-value是相關柱子的key-value值 const tooltipsDisplayTpl = ` <p class="chart-tooptip"> <span class="chart-tooptip-right">{name}</span> <span>{value}</span> </p> `; /* // 重寫tooltip元素的樣式 // 由於視覺已死,樣式是隨便搞的,就弄點黑色背景當默哀一下吧 .g2-tooltip { background-color: rgba(44, 49, 68, 0.80) !important; } .chart-tooptip { margin: 0; color: white; } .chart-tooptip-right { margin-right: 12px; } */ <Tooltip crosshairs={ false } itemTpl={ tooltipsDisplayTpl } showTitle={ false } // 去頭(標題,即橫軸對應的刻度),每每影響我顏值的不是個人身材,而是個人臉,因此不要臉了 />
note:若是想自定義tooltip展現的內容,還須要設置Geom組件的tooltip屬性,即將數據映射到Tooltip對象上;因此此屬性值若是爲false的話,就不會向Tooltip組件傳遞任何數據(此時Tooltip只會顯示title);還可設置爲字符串,展現字符串對應的數據字段;But, it's not the point,重點在於可自定義
// 定義數據返回的格式,name屬性對應的是itemTpl裏面的同名變量 const getTooltipData = (xAxis, yAxis) => { return { name: xAxis, value: yAxis }; } <Geom type="interval" position="xAxis*yAxis" tooltip={["xAxis*yAxis", getTooltipData]} />
第二個遺願也實現了,愧疚感也多了一點!最主要是寫代碼的時候總是以爲後面有人站着盯着我看。
說不定實現全部遺願就不會有這種感受了呢,那就繼續第三個遺願吧:「改變鼠標hover柱子時候柱子的顏色」,翻遍了整個文檔,發現沒有關於hover的接口啊!看來視覺是要抱恨終天了,阿門。
就在我感受到後背愈加的涼颼颼的時候,我發現Geom組件有一個屬性active
文檔就真的描述了那麼兩句話,也沒例子。急病亂投醫的我只能嘗試一波,設置爲true,得了,hover柱子的時候柱子顏色改變了!!!
<Geom type="interval" position="xAxis*yAxis" tooltip={["xAxis*yAxis", getTooltipData]} active={ true } />
那若是須要自定義鼠標hover柱子的樣式呢?對照着Geom文檔的select屬性,又嘗試了一遍
<Geom type="interval" position="xAxis*yAxis" tooltip={["xAxis*yAxis", getTooltipData]} style={{ cursor: 'pointer' }} // 鼠標hover上去的時候,顯示小手手,免費送的 active={[ true, { style: { fill: 'black', // 柱子顏色,繼續默哀 shadowColor: 'red', // 總體陰影顏色,包括邊緣 shadowBlur: 1, // 陰影的透明度 opacity: 0 // 柱子顏色透明度 } } ]} />
(神氣的)「狗子,別死了,老子搞定啦」
「靠,我都裝死兩天了,你敢不敢再慢一點」
「那官方文檔就寫了一行字:只可意會不可言傳!我天分有限,意會了比較久」
「嘚瑟,看,又出bug了吧!你的表子一閃一閃的」
(掐着他脖子使勁晃)「那TM不是bug!!!」
不過話說回來,當數據更新時,從舊數據切換到新數據,會很突兀,沒有緩衝過程,看着特別不舒服。我尋思着,在數據更新的時候,加個動畫唄!可是初始動畫生效了,更新動畫就不生效了(如看官們知道解決辦法,請不吝賜教)。因爲趕着下班,我決定使用DataSet:一個用於管理表格數據的神器,聽說更新數據的時候,其會給我弄個動畫(除此之外有方便地導入非 json 數據等等功能,下文有一些例子,具體細節我沒有詳細去研究,之後學習了再分享)。唔好理,總之好犀利!
// 安裝 // npm install @antv/data-set // 引入 import DataSet from '@antv/data-set'; // 生成一個View實例,做爲類的屬性,故不要在render方法裏面生成這個實例 dv = new DataSet().createView(); render() { this.dv.source(data); <Chart height={ 400 } data={ this.dv } forceFit ></Chart> }
「猿子,6啊!吃宵夜嗎?個人」
「雖然不怎麼餓,可是你請就不同了,go」
吃了一桶泡麪後......
「猿子,你看,宵夜也吃了·······」
「你又想幹嗎···············」
簡直就是飽飯思淫慾啊!!!
」我只是以爲柱子的顏色能夠漸變會顯得咱們公司的產品更屌一點「
」狗子,你扛揍不?「
(可憐兮兮)」揍完以後能夠加個漸變嗎......「
」......「
」我就知道你對我最好了,我給你捶背捏大腿吧~「
」滾一邊去「
<Geom type="interval" position="xAxis*yAxis" tooltip={["xAxis*yAxis", getTooltipData]} color={['xAxis', '#3DA4FF-#FFFFFF']} />
(掐着我脖子使勁晃)」老子要的是從上往下漸變,不是從左往右漸變「
「別.....別.....掐.....我....改....改.....」
<Geom type="interval" position="xAxis*yAxis" tooltip={["xAxis*yAxis", getTooltipData]} color={['xAxis', 'l(90) 0:#3DA4FF 1:#FFFFFF']} />
note:l是指線性漸變,90是指旋轉九十度(即從上到下漸變,看官們能夠多試試幾個姿式,啊呸,多試試幾個角度)
0和1標定的色值標明初始色值和終止色值,注意一點,色值不可使用顏色名字,如「red」、「blue」等
可添加多個漸變色值,如
color="l(90) 0:#000000 0.5:#FFFFFF 1:#000000"
note:若是是area類型的Geom,那麼第一種漸變方式是不起做用的,只能選用第二種
「狗子,我真要下班了」
「那個,你看都搞着漸變了,要不搞一波顏色分類」
」沒得談,goodbye!「
」宵夜個人「
」頂你個肺,又想用泡麪忽悠我!!!「
」擼串,騙你我是狗「
」emmmmmm.....「
既然提到顏色分類,咱們就接着提一下dv.transform 吧, dv.transform內置了一些基礎的函數:filter,map,pick,rename,reverse …… 具體可自行查看文檔
只須要添加 groupBy 字段,而且在傳入的原始數據data中添加對應的字段classify便可輕鬆搞定。
dv.transform({ groupBy: ['classify'], // 以classify字段進行分組 });
」猿子,顏色仍是要自定義的哦「
(白眼)
<Geom position={'xAxis*yAxis'} color={['classify', classify => { // 這裏根據不一樣字段返回不一樣顏色 return classify === 'test' ? 'red' : 'yellow'; }]} style={{ cursor: 'pointer' }} />
做爲一個有責任的前端打字員,有一點即便設計不要求的,我仍是須要說明的,橫軸的刻度值是能夠自定義的
<Axis name={xAxis} label={{ textStyle: { fill: 'red', // 顏色 textBaseline: 'top' // 對齊基線 }, formatter: (val) => { return `${ val }\n換行了` } }} />
」狗子,擼串去!!!「
」要不仍是吃個泡麪????「
狗子,卒!
很顯然,本人沒有博主這枚猿有文化底蘊,辭藻上既不華麗麗,也沒有故事性,純屬就是論事,記筆記的形式。
該需求須要用到Label
組件,她是Geon的子組件,上代碼
<div> <Chart height={400} data={dv} forceFit> <Axis name="月份" /> <Axis name="月均降雨量" /> <Legend /> <Tooltip crosshairs={{ type: "y" }} /> <Geom type="interval" position="月份*月均降雨量" color={"name"} adjust={[ { type: "dodge", marginRatio: 1 / 32 } ]} > <Label content={['月份*月均降雨量', (月份, 月均降雨量) => (`${月均降雨量}`)]} textStyle={{ textAlign: 'center', // 文本對齊方向,可取值爲: start middle end fill: '#404040', // 文本的顏色 fontSize: '14', // 文本大小 fontWeight: 'normal', // 文本粗細 rotate: 0, // 文字旋轉 textBaseline: 'top', // 文本基準線,可取 top middle bottom,默認爲middle }} /> </Geom> </Chart> </div>
效果:
import React, { PureComponent } from 'react'; import { Chart, Geom, Axis, Tooltip, Label } from 'bizcharts'; import { DatePicker } from 'antd'; import numeral from 'numeral'; import moment from 'moment'; import style from './index.less'; class AppTotalUsers extends PureComponent { numeralCount = count => numeral(count).format('0,0.00'); wanCount = count => { let data; if (count < 10000) { data = count; } else { data = this.numeralCount(count / 10000); data = `${data} 萬`; } return data; }; handleSelect = value => { const { callback } = this.props; const payload = { showDate: value }; callback(payload); }; // Can not select days before today and today disabledDate = current => current && current > moment().endOf('day'); const appChartData = [ { "x": "用戶中心管理", "y": "23715", "date": "2019-03-22" }, { "x": "測試app", "y": "2899", "date": "2019-03-22" } ]; render() { const { appChartData, showDate } = this.props; const background = { fill: '#FFFFFF', // 圖表背景色-白色 fillOpacity: 0, // 圖表背景透明度 }; const appScale = { y: { alias: '人數', type: 'pow', }, }; return ( <div style={{ height: 390 }}> <div style={{ padding: '20px', background: '#FFFFFF' }}> <Chart height={300} data={appChartData} scale={appScale} forceFit background={background}> <span className={style.mainTitle}>歷史累計用戶人數(人)</span> <DatePicker className={style.dateSelect} allowClear={false} onChange={this.handleSelect} disabledDate={this.disabledDate} // value={moment(appChartData[0] && appChartData[0].date, 'YYYY-MM-DD')} value={moment(showDate, 'YYYY-MM-DD')} /> <Axis name="x" /> <Axis name="y" visible={false} /> <Tooltip crosshairs={{ type: 'y', title: '人數', }} /> <Geom type="interval" position="x*y"> {/* 柱子上做文章用這個子組件 */} <Label content={['x*y', (x, y) => this.wanCount(`${y}`)]} textStyle={{ textAlign: 'center', // 文本對齊方向,可取值爲: start middle end fill: '#404040', // 文本的顏色 fontSize: '12', // 文本大小 fontWeight: 'normal', // 文本粗細 rotate: 0, // 文字旋轉 textBaseline: 'top', // 文本基準線,可取 top middle bottom,默認爲middle }} /> </Geom> </Chart> </div> </div> ); } } export default AppTotalUsers;
index.less文件
.chartDiv { padding: '0 20px'; background-color: '#FFFFFF'; } .mainTitle { font-weight: bold; font-size: 16px; padding: 10px; } .remark { font-weight: normal; font-size: 14px; padding-right: 30px; float: right; } .dateSelect { :global(.ant-calendar-picker-input) { border: none; } padding-right: 50px; float: right; }
效果: