现在的项目中需要做一个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 如何使用什么的,估计得找个时间另外写一票了,,,

Logo

前往低代码交流专区

更多推荐