现在的项目中需要做一个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>
<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 本身的设置等东西,为了避免在 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 如何使用什么的,估计得找个时间另外写一票了,,,