VUE 爬坑之旅 -- 在 VUE 專案中使用 ECharts 畫 K 線圖和麵積圖,並且可切換
現在的專案中需要做一個K線圖的功能,花了幾天時間查資料,讀文件,總算是基本搞定了,下面把這過程中一些需要注意的點記下來,以備不時之需。需要達到的效果如下:
說到做圖表,現在的成熟的解決方案就是百度的 ECharts 了,功能強大齊全,文件詳細,用的人多,碰到問題也好解決。老規矩,先上文件 ECharts 文件,文件內容很多,全部看一遍得花很多時間,,,看的我頭暈,,,
上週,餓了麼團隊開源了一個基於 Vue2.0 和 echarts 封裝的圖表元件 v-charts 文件地址,用了下之後發現整體上還是做的很好的,只不過如果想對圖表的細節做一些自定義或者優化的話,還是必須去看 ECharts 的文件,在這方面 v-charts 的文件顯得太簡單了,只適合對 UI 沒什麼要求的人使用。
如果專案中對圖表樣式什麼的要求比較高的,我建議還是直接用 ECharts ,ECharts 本身也可以很方便的在 vue 專案中使用,只不過需要仔細的閱讀下文件,自己根據專案需要做一下簡單的封裝就好了。
下面進入正題,
安裝,匯入並使用
首先用 npm 安裝
npm install echarts --save
ECharts 本身已經很好的支援了按需引入,在vue 元件中 import 需要的元件就可以使用了,使用前需要先給 ECharts 指定一個容器,
<template>
<!--圖表內容區域,必須給 ECharts 容器本身指定高度。不然它會使用預設高度-->
<div class = "chart">
<div id = "echarts" style = "height: 17.5rem"> </div>
</div>
</template>
<script>
import echarts from 'echarts/lib/echarts'
import 'zrender/lib/svg/svg'
import 'echarts/lib/chart/line'
import 'echarts/lib/chart/candlestick'
import chartUtil from '../../utils/chartUtil'
export default {
name: 'quotation',
data () {
return {
chart: null,
}
},
mounted () {
//初始化 ECharts 例項,不能在created生命週期內初始化,因為那時候DOM還沒有渲染,是找不到元素的
this.initChart()
},
beforeDestroy () {
//元件銷燬前先銷燬 ECharts 例項
if (!this.chart) { return }
this.chart.dispose()
this.chart = null
},
methods: {
initChart () {
// 基於準備好的dom,初始化echarts例項,移動端建議使用 svg模式
this.chart = echarts.init(document.getElementById('echarts'), 'light', {renderer: 'svg'})
this.chart.setOption(chartUtil.lineOption())
//圖示根據視窗大小自動縮放
// window.addEventListener("resize", this.chart.resize);
},
},
}
</script>
這裡有幾個需要注意的地方:
- 圖表高度,若想給圖表指定高度的話就必須給承載 ECharts 的容器指定高度,否則它會使用預設高度,給它的父容器定高也是不行的。
- 初始化時機,初始化不能在 created 生命週期裡面,因為這時候還沒有生成 DOM,所以為它指定的容器還不存在,也就無法初始化
- 在 vue 元件銷燬之前,應該要將 ECharts 例項銷燬,以避免記憶體洩漏的問題
處理資料,簡單封裝使用
上面完成了使用前的基本工作,但是現在還沒有資料,所以是沒有效果出來的,下面就弄一些資料來模擬下。除了資料之外,還有很多 ECharts 本身的設定等東西,為了避免在 vue 元件中寫入過多的程式碼而難以維護,所以我將他們單獨抽出來,寫成一個工具類,在 vue 元件中直接匯入使用就可以了
//K線圖的顏色設定
let upColor = '#D73F43'
let upBorderColor = '#D73F43'
let downColor = '#2AB180'
let downBorderColor = '#2AB180'
// 資料意義:時間,開盤(open),收盤(close),最低(lowest),最高(highest)
let data = [
['2013/3/4', 2332.08, 2273.4, 2259.25, 2333.54],
['2013/3/5', 2274.81, 2326.31, 2270.1, 2328.14],
['2013/3/6', 2333.61, 2347.18, 2321.6, 2351.44],
['2013/3/7', 2340.44, 2324.29, 2304.27, 2352.02],
['2013/3/8', 2326.42, 2318.61, 2314.59, 2333.67],
['2013/3/11', 2314.68, 2310.59, 2296.58, 2320.96],
['2013/3/12', 2309.16, 2286.6, 2264.83, 2333.29],
['2013/3/13', 2282.17, 2263.97, 2253.25, 2286.33],
['2013/3/14', 2255.77, 2270.28, 2253.31, 2276.22],
['2013/3/15', 2269.31, 2278.4, 2250, 2312.08],
['2013/3/18', 2267.29, 2240.02, 2239.21, 2276.05],
['2013/3/19', 2244.26, 2257.43, 2232.02, 2261.31],
['2013/3/20', 2257.74, 2317.37, 2257.42, 2317.86],
['2013/3/21', 2318.21, 2324.24, 2311.6, 2330.81],
['2013/3/22', 2321.4, 2328.28, 2314.97, 2332],
['2013/3/25', 2334.74, 2326.72, 2319.91, 2344.89],
['2013/3/26', 2318.58, 2297.67, 2281.12, 2319.99],
['2013/3/27', 2299.38, 2301.26, 2289, 2323.48],
['2013/3/28', 2273.55, 2236.3, 2232.91, 2273.55],
['2013/3/29', 2238.49, 2236.62, 2228.81, 2246.87],
['2013/4/1', 2229.46, 2234.4, 2227.31, 2243.95],
['2013/4/2', 2234.9, 2227.74, 2220.44, 2253.42],
['2013/4/3', 2232.69, 2225.29, 2217.25, 2241.34],
['2013/4/8', 2196.24, 2211.59, 2180.67, 2212.59],
['2013/4/9', 2215.47, 2225.77, 2215.47, 2234.73],
['2013/4/10', 2224.93, 2226.13, 2212.56, 2233.04],
['2013/4/11', 2236.98, 2219.55, 2217.26, 2242.48],
['2013/4/12', 2218.09, 2206.78, 2204.44, 2226.26],
['2013/4/15', 2199.91, 2181.94, 2177.39, 2204.99],
['2013/4/16', 2169.63, 2194.85, 2165.78, 2196.43],
['2013/4/17', 2195.03, 2193.8, 2178.47, 2197.51],
['2013/4/18', 2181.82, 2197.6, 2175.44, 2206.03],
['2013/4/19', 2201.12, 2244.64, 2200.58, 2250.11],
['2013/4/22', 2236.4, 2242.17, 2232.26, 2245.12],
['2013/4/23', 2242.62, 2184.54, 2182.81, 2242.62],
['2013/4/24', 2187.35, 2218.32, 2184.11, 2226.12],
['2013/4/25', 2213.19, 2199.31, 2191.85, 2224.63],
['2013/4/26', 2203.89, 2177.91, 2173.86, 2210.58],
['2013/5/2', 2170.78, 2174.12, 2161.14, 2179.65],
['2013/5/3', 2179.05, 2205.5, 2179.05, 2222.81],
['2013/5/6', 2212.5, 2231.17, 2212.5, 2236.07],
['2013/5/7', 2227.86, 2235.57, 2219.44, 2240.26],
['2013/5/8', 2242.39, 2246.3, 2235.42, 2255.21],
['2013/5/9', 2246.96, 2232.97, 2221.38, 2247.86],
['2013/5/10', 2228.82, 2246.83, 2225.81, 2247.67],
['2013/5/13', 2247.68, 2241.92, 2231.36, 2250.85],
['2013/5/14', 2238.9, 2217.01, 2205.87, 2239.93],
['2013/5/15', 2217.09, 2224.8, 2213.58, 2225.19],
['2013/5/16', 2221.34, 2251.81, 2210.77, 2252.87],
['2013/5/17', 2249.81, 2282.87, 2248.41, 2288.09],
]
//處理資料,分別拿到分時圖和K線圖的資料
let lineData = sliceLineData(data)
let candleData = sliceCandleData(data)
function sliceCandleData (data) {
let categoryData = []
let values = []
for (let i = 0; i < data.length; i++) {
categoryData.push(data[i].slice(0, 1)[0]);
values.push(data[i].slice(1))
}
return {
categoryData: categoryData,
values: values,
}
}
function sliceLineData (data) {
let categoryData = []
let values = []
for (let i = 0; i < data.length; i++) {
categoryData.push(data[i].slice(0, 1)[0])
values.push(data[i].slice(1, 2)[0])
}
return {
categoryData: categoryData,
values: values,
}
}
//計算 MA 均線的資料
function calculateMA (dayCount) {
let result = []
for (let i = 0, len = candleData.values.length; i < len; i++) {
if (i < dayCount) {
result.push('-')
continue
}
let sum = 0
for (let j = 0; j < dayCount; j++) {
sum += candleData.values[i - j][1]
}
result.push(sum / dayCount)
}
return result
}
//面積圖 圖表設定
let lineSeries = [
{
type: 'line',
data: lineData.values,
smooth:true,
itemStyle:{
color:'#354162',
opacity:0.1
},
lineStyle:{
width:1,
color:'#354162'
},
areaStyle: {
color:'#EEEEEE'
},
},
]
//K線圖 圖表設定
let candleSeries = [
{
type: 'candlestick',
data: candleData.values,
// barWidth:5,
itemStyle: {
normal: {
color: upColor,
color0: downColor,
borderColor: upBorderColor,
borderColor0: downBorderColor,
},
},
},
]
//共用的一些圖表設定
let option = {
grid: {
top: 10,
bottom: 20,
left: 10,
right: 10,
},
xAxis: {
data: undefined,
scale: true,
axisLabel: {
color: '#A0A0A0',
fontSize: 10,
},
axisLine: {
lineStyle: {
color: '#A0A0A0',
},
},
},
yAxis: {
scale: true,
position: 'right',
axisLabel: {
color: '#A0A0A0',
fontSize: 10,
inside: true,
},
axisLine: {
lineStyle: {
color: '#A0A0A0',
},
},
splitLine: {
lineStyle: {
color: '#EEEEEE',
},
},
},
series: undefined,
}
export default class chartUtil {
static lineOption = () => {
option.xAxis.data = lineData.categoryData
option.series = lineSeries
return option
}
static candleOption = () => {
option.xAxis.data = candleData.categoryData
option.series = candleSeries
return option
}
}
上面就是工具類的完整程式碼,對 ECharts 來說,最重要的就是 option ,option 裡面包含了渲染圖表所需要的資料,各種屬性等東西。
option 裡面各屬性的含義我這裡就不多說了,不清楚的就去看下文件,因為每個專案的要求都不一樣,肯定是需要根據自己的需求對 option 進行相應的修改的。
這裡只有一點需要特別說明下的就是 grid 這個屬性,這個屬性的作用是指定圖表內容到它的容器的四個邊的距離,如果不修改 grid 的話,效果是這樣的
可以看到,圖表內容距離四邊都有一段不小的距離,要怎麼減小這個距離呢,就是通過 grid 屬性來設定的。
在我的專案中,因為要顯示分時圖和K線圖二種圖表,而且還要進行切換,所以我將 option.xAxis.data 和 option.series 這兩個需要變動的部分抽出來以實現切換的功能,這也就是簡單封裝下,大家可以根據自己的專案需要來進行相應的封裝。
這樣,基本工作就做完了,在需要切換圖表的時候,只需要給 ECharts 設定不同的 option 就可以了
if (index === 0) {
this.chart.setOption(chartUtil.lineOption())
} else {
this.chart.setOption(chartUtil.candleOption())
}
以上,就是這次使用 ECharts 後的總結,當然,現在這樣做出來的只是靜態頁面,一般這種K線圖的資料需要從伺服器實時獲取,現在主流的做法是採用 WebSocket 來讓客戶端和伺服器之間保持一個長連結,以達到及時推送資料的目的。
WebSocket 如何使用什麼的,估計得找個時間另外寫一票了,,,