在近期的項目中,有大量處理可視化數據的需求。提及這個,相信不少同窗跟我同樣,都會想到用 Echarts 來實現。沒錯,Echarts 擁有高度可定製化的配置,以及很是詳盡的開發文檔,而且它的最新版已經更新到了 v4.3。css
不過,也是由於 Echarts 的開發文檔過於龐雜,對於不熟悉 Echarts 的同窗來講,在查找某個效果時,可能須要耗費大量的精力。雖然它也配備了一些官方實例,但或許每每只能借鑑其中的個別配置,還須要回到對應的文檔中作印證,另外還得本身作 demo,嘗試效果。。。踩坑的過程,很艱辛。html
但同時也是收穫滿滿的。今天的這篇文章,主要總結近段時間結合 Echarts 實現數據可視化的一些心得體會。其實只須要一些小技巧,就能實現下面這樣 「美美噠」 的圖表了。前端
但願給有須要的同窗一些啓發,相信你也能夠定製出本身專屬的 Echarts 數據可視化風格。git
在踩坑的期間,我查找了很多資料。發現一個不太友好的體驗:一些文章中僅僅敘述了某個效果的實現,卻沒提到它的當前版本。當我拿來嘗試的時候,卻發現怎麼也出不來效果,並且運氣很差的話,還會報錯,這讓我很無奈。es6
因此,爲了不無謂的麻煩。首先須要申明,本篇文章中的各類配置以及它的效果,是基於 Echarts 目前的最新版 V4.3 實現的。github
而後,由於項目採用了 Vue 搭建,因此我搭配了 v-charts 來實現圖表效果。v-charts 是由 「餓了麼前端」 開發維護的基於 Vue2.0 和 Echarts 封裝的圖表組件。我用下來感受挺不錯的,文檔很清晰,還有相配合的例子,很是容易上手。它目前的最新版是 v1.19.0。apache
v-charts 通常經常使用的圖表有:ve-line(折線圖)、ve-histogram(柱狀圖)、ve-pie(餅圖)、ve-ring(環圖)等等。使用時,能夠直接將單個圖表引入到項目中。數組
import VeLine from 'v-charts/lib/line.common';
Vue.component(VeLine.name, VeLine);
複製代碼
一種典型的 v-charts 的 data 屬性數據格式以下:bash
{
columns: ['日期', '訪問用戶', '下單用戶'],
rows: [
{ '日期': '2018-05-22', '訪問用戶': 32371, '下單用戶': 19810 },
{ '日期': '2018-05-23', '訪問用戶': 12328, '下單用戶': 4398 },
{ '日期': '2018-05-24', '訪問用戶': 92381, '下單用戶': 52910 }
]
}
複製代碼
其中,columns 中是維度和指標的集合,v-charts 中的大部分圖表都是單維度多指標,因此默認第一個值爲 維度,剩餘的值爲指標。另外,rows 中是數據的集合。數據結構
再舉一個柱狀圖與折線圖組合的典型例子。
它的代碼實現也十分簡單,只需額外配置 settings 參數便可,代碼以下:
<template>
<ve-histogram :data="chartData" :settings="chartSettings"></ve-histogram>
</template>
<script>
export default {
data () {
this.chartSettings = {
axisSite: { right: ['下單率'] },
yAxisType: ['KMB', 'percent'],
yAxisName: ['數值', '比率'],
showLine: ['下單率'],
}
return {
chartData: {
columns: ['日期', '訪問用戶', '下單用戶', '下單率'],
rows: [
{ '日期': '1/1', '訪問用戶': 1393, '下單用戶': 1093, '下單率': 0.32 },
{ '日期': '1/2', '訪問用戶': 3530, '下單用戶': 3230, '下單率': 0.26 },
{ '日期': '1/3', '訪問用戶': 2923, '下單用戶': 2623, '下單率': 0.76 },
{ '日期': '1/4', '訪問用戶': 1723, '下單用戶': 1423, '下單率': 0.49 },
{ '日期': '1/5', '訪問用戶': 3792, '下單用戶': 3492, '下單率': 0.323 },
{ '日期': '1/6', '訪問用戶': 4593, '下單用戶': 4293, '下單率': 0.78 }
]
}
}
}
}
</script>
複製代碼
因此,若是沒有很強烈的定製化需求的話,v-charts 已經能夠知足大多數的基礎需求了。
但若是想實現本文開頭的那張圖中的效果,還須要在 v-charts 的 extends
屬性上花一番功夫。extends 在本質上,對應的就是 Echarts 文檔的 配置項 中的全部屬性。
接下來,會在 v-charts 的基礎配置之上,具體地介紹一些很實用的小技巧,來幫助你實現 Echarts 的深度定製化需求。
Echarts 提供了 colors 來自定義顏色,它接收一個數組做爲結果。這裏有一份來自產品小姐姐的 UI 配色方案,已經能夠適用於絕大多數場景了。
代碼以下:
colors: ['#60ACFC', '#35C5EB', '#4DBECF', '#65D5B2', '#5BC4A0', '#9DDD81', '#D4ED58', '#FFDB43', '#FEB54E', '#FF9D68']
複製代碼
v-charts 中的 x 軸實線看上去有點生硬,能夠將它變爲虛線,看上去就會清爽不少了,具體能夠配置 setOption 下的 yAxis 屬性。
yAxis (item) {
item[0].splitLine = Object.assign({}, {
lineStyle: {
color: '#e1e2e2',
type: 'dashed'
}
});
return item;
}
複製代碼
這裏用到了 es6 中 function 的簡寫模式。其中的 item 指的是 y 軸,爲何是個數組呢?那是由於通常狀況下單個直角座標系 grid 組件最多隻能放左右兩個 y 軸。那麼 item[0] 是指左側的最經常使用的 y 軸,item[1] 是指當有雙軸狀況下的右側的 y 軸。
這裏核心是要設置 splitLine 下的 lineStyle,效果就如開篇的第一張圖那樣:
固然,也能夠定製 tooltip的虛線,效果以下:
只須要配置 tooltip 下 axisPointer 的 lineStyle 便可,代碼很簡單:
tooltip : {
axisPointer: {
lineStyle: {
color: '#e1e2e2',
type: 'dashed',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 5
}
}
}
複製代碼
堆疊圖與面積圖在剛接觸時容易混淆,它們其實是兩種不一樣的效果。
堆疊圖中的後一項數據會疊在前一項數據之上,像下面這樣:
能夠經過設置 v-charts 提供的 extend 屬性進行配置。以前也說過,它本質上就是配置 Echarts 中的 setOption。
咱們分別對柱狀圖和折線圖設置了堆疊效果,能夠發現 e.stack 的值是分開設置的,否則的話在數值上就會再次疊加。
<ve-histogram :extend="chartExtend"></ve-histogram>
...
this.chartExtend = {
series: (v) => {
Array.from(v).forEach((e, idx) => {
if (e.type === 'bar') {
e.stack = 'bar';
}
if (e.type === 'line') {
e.stack = 'line';
}
};
},
...
};
複製代碼
而面積圖,每每在折線圖中是爲了突出某一塊區域的對比。
一樣地,在 chartExend 中進行配置:
<ve-line :extend="chartExtend"></ve-line>
...
this.chartExtend = {
series: (v) => {
Array.from(v).forEach((e, idx) => {
// 將指定條件下的折線設置爲面積圖
if (...) {
e.areaStyle = 'line';
}
};
},
...
};
複製代碼
圖例,在 Echarts 的配置項中,也叫 legend。經過它能夠直觀地看到不一樣顏色對應的數據分佈狀況。同時,它也是包含點擊交互的,當你暫時不想關心某個數據項是,能夠點擊對應的圖例,讓它暫時隱藏,再次點擊,又會從新渲染出來。
我遇到過更進一步地需求:在柱狀圖的圖例中,要默認展現當天的數據,當點擊某一天時柱子時,圖例中的數據要隨之變化。需求的目的是,讓用戶能更直觀地瞭解每一天的數據,當他須要看某兩天的數據對比時,只須要點擊柱子切換數據便可。效果以下:
當想看 9月16日的數據時,點擊對應的柱子,則圖例中的數據會刷新,同時左上角的時間也會隨之變化:
實現的思路基本是這樣的:
匹配當日數據的具體方法,由於業務邏輯的不一樣,數據結構也會不一樣,這裏就不細說了。我把事件回調的寫法,還有自定義模板的輸出的代碼貼出來,給你們參考一下:
<ve-histogram :data="chartData" :extend="chartExtend" :events="{ click: handleChartEvents }"></ve-histogram>
...
handleChartEvents (e) {
const date = e.name;
// 設置左上角的時間(帶格式化),這裏再也不展開
this.setCurrentDate(date);
this.setChartExtend();
},
...
setChartExtend () {
this.chartExtend = {
legend: {
formatter: (name) => {
// currentNumber 是已經匹配到的當日數據
const result = [
`{a|${name}}`,
`{b|${currentNumber}}`
];
return result.join('\n');
},
textStyle: {
height: 42,
rich: {
a: {
fontSize: 12,
align: 'left',
padding: [0, 10, 15, 0]
},
b: {
fontSize: 24,
align: 'left',
padding: [0, 10, 0, 0],
lineHeight: 40,
width: 60
}
}
},
// 圖例標記的圖形寬度
itemWidth: 14,
// grid 距離整個容器頂部的高度
grid: {
top: 120
}
},
...
};
},
...
複製代碼
點擊事件的回調裏,能夠拿到具體的時間 name,而後交給 setCurrentDate 方法作時間格式化的設置,並渲染到界面上,同時它也是後續匹配當日數據的依據。
在 legend 的 formatter 方法中組裝的自定義模板,分別有兩個樣式 a 和 b,你也能夠取別的樣式名字,但要記得對應地在 textStyle 的 rich 對象裏設置樣式。
rich 是富文本樣式的自定義寫法,有點相似於平時常見的 css,但像 padding 之類的樣式寫法,又略有不一樣,並且調試起來並不容易。上述代碼中的數字,是通過屢次微調以後的結果。
最後的 itemWidth 是爲了設置顏色塊的寬度,使之變爲方形,更加美觀。由於圖例中添加的數字的樣式,因此總體的高度變得大了。grid 的 top,能夠調整主繪製區域到整個容器頂部的距離,從而使之距離圖例,也能留出更合理的空白高度。
除此以外,對於數據較多的圖表,若是剛加載就展現所有數據的話,會比較亂。
這時,咱們就想,是否能夠只展現其中某一類的數據呢?像下面這樣:
這裏,就須要用到 legend 的 selected 屬性。它是一個對象,意思是圖例選中狀態表。用法很簡單:
<ve-line :extend="chartExtend"></ve-line>
...
setChartExtend () {
this.chartExtend = {
legend: {
formatter: (name) => {
return `{a|${name}}`;
},
textStyle: {
// 這裏的 rich 樣式,是爲了四等分的佈局
rich: {
a: {
width: 240,
height: 20,
lineHeight: 22,
align: 'left'
}
}
},
width: 1090,
// 圖例標記的圖形寬度
itemWidth: 14,
selected: {
'T1-第7日': false,
'T1-第14日': false,
'T1-第30日': false,
'T2-第7日': false,
'T2-第14日': false,
'T2-第30日': false,
'T3-第7日': false,
'T3-第14日': false,
'T3-第30日': false,
}
},
...
};
}
複製代碼
其中的 formatter 函數返回一個帶樣式的文本,經過樣式將佈局四等分。而後設置 selected,將默認不須要展現的數據隱藏起來。
注意,selected 對象中的 key 均爲指標的具體名稱,不能用別名代替,不然是出不來效果的。
剛接到需求要實現 mini 圖的時候,第一時間想到的是下面這張圖的模樣:
這是一個不一樣緩動函數效果的官方示例,每個緩動效果圖都帶有一個標題組件。
研究了 示例代碼 後,發現它的實現原理是:模擬了輸入 x 軸的不斷變大的值,經過不一樣的效果函數,來輸出對應的 y 軸的值,達到呈現出不一樣的效果。
很遺憾,這無法不適用於咱們的需求。咱們但願實現一個迷你的單一數據趨勢圖,沒有任何座標軸,但數據的趨勢並不必定是線性增加的。然而,值得慶幸的是,能夠借鑑其中的隱藏座標軸的作法。具體的實現,仍是要設置 extend,代碼以下:
<ve-line :data="chartData" :extend="chartExtend" width="120px" height="40px">
...
this.chartExtend = {
xAxis: {
show: false,
},
yAxis: {
show: false,
},
legend: {
show: false
},
grid: {
show: true,
left: 0,
top: 5,
width: '100%',
height: '100%',
borderWidth: 0,
backgroundColor: '#fff',
},
series: (v) => {
Array.from(v).forEach((e, idx) => {
e.symbol = 'circle';
e.symbolSize = 2;
e.areaStyle = { opacity: 0.3 };
});
return v;
},
tooltip: {
formatter: (params) => {
let result = '';
params.forEach(param => {
let str = `${param.marker}${param.value[0]} ${param.value[1]}<br>`;
result += str;
});
return result;
}
}
};
複製代碼
由於是迷你的圖表,因此須要在佈局中,將它調整爲可控的大小。相應的,grid 屬性的 top 和 left,也能夠幫咱們作微調。x 軸,y 軸,圖例,這些都隱藏掉。最後,爲了支持 tooltip,對其結構進行了 formatter。最終的呈現效果以下:
正由於 mini 圖的尺寸足夠的小,因此比較適合於對某些重要數據,作週期性的趨勢解讀,並能夠位於重要數據的附近。這也能使得界面的佈局,看上去更爲的協調。
當一張圖表中,存在雙軸時,很大機率下都會出現 2 個 y 軸的刻度不一致的狀況。
想一想也是啊,設置爲雙軸的緣由,原本就是由於它們的數值差別過大。若是不設置雙軸,某些數據就看不到了,並非沒有渲染,而是由於過小,致使其被數值大的數據 「淹沒」 了。
一種解決方案是,須要本身去定義 y 軸的 max,min,interval。由於系統不會再自動計算適配了。
yAxis:[
{......},
{
type: 'value',
name: '銷售額(元)',
min: 0,
max: max, // 計算最大值
interval: Math.ceil(max / 5), // 平均分爲5份
axisLabel: {
formatter: '{value}'
}
]
複製代碼
這個方案能夠將雙軸的刻度重合在一塊兒,實現 「單線」 的效果。可是,由於要本身算,因此略微有些麻煩,均分的份數可能會由於界面的美觀,須要進行調整。若是雙軸的單位不一致,好比左側爲數值,右側爲百分比,也挺麻煩的。
因此,就想了一個 「偷懶」 的辦法。既然只要展現 「單線」,那就把第二根線隱藏起來唄,其實儘管是 「雙線」,但其實它們之間離得仍是挺近的,從效果上來看,並無太大的影響。
實現方案也很簡單,還記得以前介紹 「虛線」 是如何設置的麼?
yAxis (item) {
item[0].splitLine = Object.assign({}, {
lineStyle: {
color: '#e1e2e2',
type: 'dashed'
}
});
return item;
}
複製代碼
其中的 item[0] 表明的是左側最經常使用的 y 軸。那麼右側的天然就是 item[1] 了,把它隱藏起來就好了:
yAxis (item) {
item[0].splitLine = Object.assign({}, {
...
});
item[1].splitLine = {
show: false
};
return item;
},
複製代碼
是否是很簡單?其實知道了對的方法,實現起來仍是簡直就是 easy 模式。但每每探究 「對的方法」 須要耗費大量的精力。
今天介紹了幾種 Echarts 定製化的方案,可能是由實際工做中提煉總結出來的小技巧,但願能幫到有須要的同窗。若是你以爲本文還不錯,還請點個 「贊」 或者 「關注」,以便讓更多有須要的同窗看到,感謝支持!
可能細心的同窗會問:本文開頭的那張圖中的「小圖標」是如何實現的呢?文章中貌似沒有涉及到。
是的。因爲篇幅的關係,我會單獨再開一篇,專門來聊一聊關於 「標記」 的定製化方案,歡迎你們屆時捧場,感謝!
PS:歡迎關注個人公衆號 「超哥前端小棧」,交流更多的想法與技術。