1. 程式人生 > >VUE 爬坑之旅 -- 在 VUE 專案中使用 ECharts 畫 K 線圖和麵積圖,並且可切換

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 如何使用什麼的,估計得找個時間另外寫一票了,,,