这章节将完成我们的分时图,并使用真实的数据来进行展示分时图。
一天的交易时间段分为上午的09:30~11:30,下午的13:00~15:00两个时间段,因为分时间段的关系,数据是不连续的,所以会先分为2个grid,上午的grid跟下午的grid,又因为分时图是由折线图跟柱状图来组成的,所以又把上下午的grid再一分为二,变成了4个grid,这也是为什么会分解成4个grid的原因。
先看看分时图的数据。
现在我们再重新看一下雪球的分时图
根路径的last_price指的是昨日收盘价,这个数值很重要,我们需要这个数值来建立一条基准线;然后items里面就是今天的交易数据了,current表示这一分钟应该取的价格,一般是指一分钟内最后一笔成交的价格,也是折线图的数据;volume则是这一分钟的成交量,也就是下方柱状图的数据;timestamp表示这是哪个时间的数据,我们用来作为x轴的数据。
将x轴的type设置成“time”。为了方便我们也把坐标轴的最大最小值设置成随数据的最大最小值,把max设置成"dataMax",min设置成"dataMin"。
type: 'time', max:'dataMax', min:'dataMin'
y轴也是需要进行一些配置,已达到完全对称的效果,需要配置interval属性以及max,min属性。x轴的时间是已经固定了09:30~11:30,13:00~15:00,所以可以直接写死,但是y轴的价格是会波动的,所以这三个数值是要经过运算得来的。
我们先配置上方的折线图的y轴坐标轴线为9条,设置splitNumber属性为9即可,定义变量priceMax、priceMin、priceInterval(priceInterval表示价格的差值),以及左边的数据数组grid1Data,右边的数据数组grid3Data。(注:由于x轴的type修改成了'time'类型,所以去掉x轴的data属性)。下方的柱状图也是这样,下方的柱状图一共是3条坐标轴线,设置splitNumber属性为3即可,定义变量volumeMax,volumeMin,volumeInterval(volumeInterval表示价格的差值),以及左边的数据数组grid2Data,右边的数据数组grid4Data;柱状图每一条柱子的颜色设置方式为
color: function (params) {
// params.dataIndex 柱子的下标 return "#000"; }
所以要新建两个数组volumeColor1,volumeColor2来存放柱子的颜色。接下来,我们要遍历我们拿到的分时图数据data,来组成我们需要的数据。
for(var i in data.data.items){ // 上午的数据 if(i < 121){ if(data.data.items[i].current > priceMax){ priceMax = data.data.items[i].current; } if(data.data.items[i].current < priceMin || priceMin == 0){ priceMin = data.data.items[i].current; } // 左上方折线图 grid1Data.push([data.data.items[i].timestamp,data.data.items[i].current]); if(data.data.items[i].volume > volumeMax){ volumeMax = data.data.items[i].volume; } if(data.data.items[i].volume < volumeMin ){ volumeMin = data.data.items[i].volume; } if(i == 0){ if(data.data.items[i].current >= lastPrice){ volumeColor1.push(UP_COLOR); }else{ volumeColor1.push(DOWN_COLOR); } }else{ if(data.data.items[i].current >= data.data.items[i-1].current){ volumeColor1.push(UP_COLOR); }else{ volumeColor1.push(DOWN_COLOR); } } // 左下方柱状图 grid2Data.push([data.data.items[i].timestamp,data.data.items[i].volume]); }else{// 下午的数据 if(data.data.items[i].current > priceMax){ priceMax = data.data.items[i].current; } if(data.data.items[i].current < priceMin || priceMin == 0){ priceMin = data.data.items[i].current; } // 右上方折线图 grid3Data.push([data.data.items[i].timestamp,data.data.items[i].current]); if(data.data.items[i].volume > volumeMax){ volumeMax = data.data.items[i].volume; } if(data.data.items[i].volume < volumeMin){ volumeMin = data.data.items[i].volume; } if(data.data.items[i].current >= data.data.items[i-1].current){ volumeColor2.push(UP_COLOR); }else{ volumeColor2.push(DOWN_COLOR); } // 右下方柱状图 grid4Data.push([data.data.items[i].timestamp,data.data.items[i].volume]); } }
遍历完成后,得到了价格的最大最小值,成交量的最大值,接下来还要处理一下价格的最大最小值,以达到对称的效果。列入,昨日收盘价是1,今日最高价是上涨了0.5块,最高价是1+0.5 = 1.5块,那么为了达到对称的效果,下方的价格线应该是1-0.5=0.5块。
// 重新计算价格的最大最小值,以达到对称的效果 if((lastPrice - priceMax) * -1 > (lastPrice - priceMin)){ priceMin = (lastPrice - ((lastPrice - priceMax)* -1)); }else{ priceMax =(lastPrice + (lastPrice - priceMin)); } priceInterval = (priceMax - lastPrice) / 4; volumeInterval = volumeMax / 2;
重新计算了最大最小价格,并得到了价格的差值,成交量的差值,把这些数值设置到对应的位置上。第一个跟第三个grid的y轴属性max,min,interval分别设置成priceMax,priceMin,priceInterval;第二个跟第四个grid的y轴属性max,min,interval分别设置成volumeMax,0,volumeInterval。然后我们会发现,y轴上的值的小数位非常大,x轴上显示的是月日时分的格式,所以还要对这些显示的数值进行格式化。首先对y轴的数据进行格式化,左上方的y轴设置:
axisLabel:{ fontSize:10, margin:0, // y轴的数值向内显示 align:"left", formatter: function (value, index) { return value.toFixed(2); }, color: function (value, index) { // 中间基准线的数值为黑色 if(parseFloat(value).toFixed(2) == lastPrice){ return NORMAL_COLOR; } // 上涨区域的数字为红色 if(value > lastPrice){ return UP_COLOR; } // 下方下跌的数值为绿色 if(value < lastPrice){ return DOWN_COLOR; } } },
右上方的y轴的设置:
axisLabel:{ fontSize:10, margin:0, // y轴的数值向内显示 align:"right", formatter: function (value, index) { var persent = (value - lastPrice) / lastPrice; persent = (persent < 0) ? persent * -1 : persent; persent = persent * 100; return persent.toFixed(2) + '%'; }, color: function (value, index) { // 中间基准线的数值为黑色 if(parseFloat(value).toFixed(2) == lastPrice){ return NORMAL_COLOR; } // 上涨区域的数字为红色 if(value > lastPrice){ return UP_COLOR; } // 下方下跌的数值为绿色 if(value < lastPrice){ return DOWN_COLOR; } } },
下方的柱状图的y轴数值则隐藏不显示,只需要把第二个第四个grid的y轴的axisLabel.show设置成false即可
axisLabel:{ //设置显示坐标轴的数值为不显示 show:false },
然后重新对x轴的时间进行格式化成HH:mm的格式,只需要对第二个跟第四个grid的x轴进行配置即可。
第二个grid的x轴的配置
axisLabel: { fontSize:12, show: true, color:'#888', formatter: function (value) { var a = echarts.format.formatTime('hh:mm', value); if(a == "11:30"){ return ""; } if(a == "09:30"){ return " 09:30"; } return a; } },
第四个grid的x轴的配置
axisLabel: { fontSize:12, show: true, showMinLabel:false, color:'#888', formatter: function (value) { var a = echarts.format.formatTime('hh:mm', value); if(a == "13:00"){ return "11:30/13:00"; } if(a == "15:00"){ return "15:00 "; } return a; } },
接下来配置一下柱状图的柱子颜色。在series里找到柱状图的配置,对itemStyle进行配置即可。
第一个柱状图的配置
itemStyle:{ normal: { color: function (params) { return volumeColor1[params.dataIndex]; } } }
第二个柱状图的配置
itemStyle:{ normal: { color: function (params) { return volumeColor2[params.dataIndex]; } } }
最后我们把各个坐标轴的小尖刺,就是刻度值旁边的短线去掉。把各个坐标轴的axisTick.show设置成false即可。
axisTick:{show:false}
做完这一步后,我们的代码就成了这样
// 雪球json数据 https://stock.xueqiu.com/v5/stock/chart/minute.json?symbol=SH000001&period=1d var jsonData = ''; var data = JSON.parse(jsonData); // 第一个grid的数据(折线图) var grid1Data = []; // 第二个grid的数据(柱状图) var grid2Data = []; // 第三个grid数据(折线图) var grid3Data = []; // 第四个grid数据(柱状图) var grid4Data = []; // 柱状图的颜色 // 柱状图的红绿规则比较麻烦,所以本次采用的规则则是根据价格的涨跌来区分 var volumeColor1 = []; var volumeColor2 = []; var UP_COLOR = "#E24528"; var DOWN_COLOR = "#009933"; var NORMAL_COLOR = "#33353C"; var priceMax = 0,priceMin = 0,priceInterval = 0,volumeMax = 0,volumeMin = 0,volumeInterval = 0; var lastPrice = data.data.last_close; initData(); function initData(){ for(var i in data.data.items){ // 上午的数据 if(i < 121){ if(data.data.items[i].current > priceMax){ priceMax = data.data.items[i].current; } if(data.data.items[i].current < priceMin || priceMin == 0){ priceMin = data.data.items[i].current; } // 左上方折线图 grid1Data.push([data.data.items[i].timestamp,data.data.items[i].current]); if(data.data.items[i].volume > volumeMax){ volumeMax = data.data.items[i].volume; } if(data.data.items[i].volume < volumeMin ){ volumeMin = data.data.items[i].volume; } if(i == 0){ if(data.data.items[i].current >= lastPrice){ volumeColor1.push(UP_COLOR); }else{ volumeColor1.push(DOWN_COLOR); } }else{ if(data.data.items[i].current >= data.data.items[i-1].current){ volumeColor1.push(UP_COLOR); }else{ volumeColor1.push(DOWN_COLOR); } } // 左下方柱状图 grid2Data.push([data.data.items[i].timestamp,data.data.items[i].volume]); }else{// 下午的数据 if(data.data.items[i].current > priceMax){ priceMax = data.data.items[i].current; } if(data.data.items[i].current < priceMin || priceMin == 0){ priceMin = data.data.items[i].current; } // 右上方折线图 grid3Data.push([data.data.items[i].timestamp,data.data.items[i].current]); if(data.data.items[i].volume > volumeMax){ volumeMax = data.data.items[i].volume; } if(data.data.items[i].volume < volumeMin){ volumeMin = data.data.items[i].volume; } if(data.data.items[i].current >= data.data.items[i-1].current){ volumeColor2.push(UP_COLOR); }else{ volumeColor2.push(DOWN_COLOR); } // 右下方柱状图 grid4Data.push([data.data.items[i].timestamp,data.data.items[i].volume]); } } // 重新计算价格的最大最小值,以达到对称的效果 if((lastPrice - priceMax) * -1 > (lastPrice - priceMin)){ priceMin = (lastPrice - ((lastPrice - priceMax)* -1)); }else{ priceMax =(lastPrice + (lastPrice - priceMin)); } priceInterval = (priceMax - lastPrice) / 4; volumeInterval = volumeMax / 2; setOptions(); } function setOptions(){ // 初始化一个echarts的对象 var chart = echarts.init(document.getElementById('charts')); // echarts折线图的配置项 var option = { // grid grid:[ // 第一个grid { top:10,// 图表的外边距 height:200,// 图表的高度 left:'0', width:'50%',//因为是左右各一个图表,使用百分比的方式显得更方便, }, // 第二个grid,第二个图表是在第一个图表的下方,所以要把它定位到底部 { top:240,//设置上方的外边距是第一个图表的高度再加10,使用top是方便我们调整下方grid的高度 left:'0', width:'50%',// 宽度与第一个图表一个大 height:100 }, // 第三个grid,第三个图表是在第一个图表的右方,所以要把它定位到右方 { top:10,// 图表的外边距 left:'50%',//设置右边图表的左边距是第一个图表的大小,达到定位右边的效果 width:'50%',// 宽度与第一个图表一个大 height:200 }, // 第四个grid,第四个图表是在第三个图表的下方,所以要把它定位到底部 { top:240,//设置上方的外边距是第三个图表的高度再加10,使用top是方便我们调整下方grid的高度 left:'50%',//设置右边图表的左边距是第三个图表的大小,达到定位右边的效果 width:'50%',// 宽度与第一个图表一个大 height:100 } ], // 多个图表则会存在对个x轴y轴,所以这里的配置我们也换成数组的方式 // x轴配置, xAxis:[ // 第一个grid的x轴属性 { // 告诉echarts,这个第一个grid的x轴 gridIndex:0, // 坐标轴是否留白 boundaryGap:false, // x轴的刻度 axisTick:{show:false}, // x轴的刻度值 axisLabel:{show:false}, max:'dataMax', min:'dataMin', type: 'time', axisLine:{ lineStyle:{ color:"#ECEEF2" } }, splitLine:{ lineStyle:{ color:"#ECEEF2", // 设置线条喂风格为虚线 type:"dashed" } }, }, // 第二个grid的x轴属性 { // 告诉echarts,这个第一个grid的x轴 gridIndex:1, // 坐标轴是否留白 boundaryGap:false, // x轴的刻度 axisTick:{show:false}, max:'dataMax', min:'dataMin', type: 'time', axisLabel: { fontSize:12, show: true, color:'#888', formatter: function (value) { var a = echarts.format.formatTime('hh:mm', value); if(a == "11:30"){ return ""; } if(a == "09:30"){ return " 09:30"; } return a; } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, splitLine:{ lineStyle:{ color:"#ECEEF2", // 设置线条喂风格为虚线 type:"dashed" } }, }, // 第三个grid的x轴属性 { // 告诉echarts,这个第一个grid的x轴 gridIndex:2, // 坐标轴是否留白 boundaryGap:false, // x轴的刻度 axisTick:{show:false}, // x轴的刻度值 axisLabel:{show:false}, type: 'time', max:'dataMax', min:'dataMin', axisLine:{ lineStyle:{ color:"#ECEEF2" } }, splitLine:{ lineStyle:{ color:"#ECEEF2", // 设置线条喂风格为虚线 type:"dashed" } }, }, // 第四个grid的x轴属性 { // 告诉echarts,这个第一个grid的x轴 gridIndex:3, // 坐标轴是否留白 boundaryGap:false, // x轴的刻度 axisTick:{show:false}, type: 'time', max:'dataMax', min:'dataMin', axisLabel: { fontSize:12, show: true, showMinLabel:false, color:'#888', formatter: function (value) { var a = echarts.format.formatTime('hh:mm', value); if(a == "13:00"){ return "11:30/13:00"; } if(a == "15:00"){ return "15:00 "; } return a; } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, splitLine:{ lineStyle:{ color:"#ECEEF2", // 设置线条喂风格为虚线 type:"dashed" } }, } ], // y轴配置 yAxis: [ // 第一个grid的y轴属性 { // 去掉刻度值旁边的指示线 axisTick:{show:false}, splitNumber:9, gridIndex:0, interval:priceInterval, max:priceMax, min:priceMin, splitLine:{ lineStyle:{ color:"#ECEEF2", // 设置线条喂风格为虚线 type:"dashed", } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, axisLabel:{ fontSize:10, margin:0, // y轴的数值向内显示 align:"left", formatter: function (value, index) { return value.toFixed(2); }, color: function (value, index) { // 中间基准线的数值为黑色 if(parseFloat(value).toFixed(2) == lastPrice){ return NORMAL_COLOR; } // 上涨区域的数字为红色 if(value > lastPrice){ return UP_COLOR; } // 下方下跌的数值为绿色 if(value < lastPrice){ return DOWN_COLOR; } } }, }, // 第二个grid的y轴属性 { // 去掉刻度值旁边的指示线 axisTick:{show:false}, splitNumber:3, gridIndex:1, interval:volumeInterval, max:volumeMax, min:0, splitLine:{ lineStyle:{ color:"#ECEEF2", // 设置线条喂风格为虚线 type:"dashed" } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, axisLabel:{ //设置显示坐标轴的数值为不显示 show:false }, }, // 第三个grid的y轴属性 { // 去掉刻度值旁边的指示线 axisTick:{show:false}, splitNumber:9, position:'right', gridIndex:2, interval:priceInterval, max:priceMax, min:priceMin, splitLine:{ lineStyle:{ color:"#ECEEF2", // 设置线条喂风格为虚线 type:"dashed" } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, axisLabel:{ fontSize:10, margin:0, // y轴的数值向内显示 align:"right", formatter: function (value, index) { var persent = (value - lastPrice) / lastPrice; persent = (persent < 0) ? persent * -1 : persent; persent = persent * 100; return persent.toFixed(2) + '%'; }, color: function (value, index) { // 中间基准线的数值为黑色 if(parseFloat(value).toFixed(2) == lastPrice){ return NORMAL_COLOR; } // 上涨区域的数字为红色 if(value > lastPrice){ return UP_COLOR; } // 下方下跌的数值为绿色 if(value < lastPrice){ return DOWN_COLOR; } } }, }, // 第四个grid的y轴属性 { // 去掉刻度值旁边的指示线 axisTick:{show:false}, splitNumber:3, position:'right', gridIndex:3, interval:volumeInterval, max:volumeMax, min:0, axisLabel:{ //设置显示坐标轴的数值为不显示 show:false }, splitLine:{ lineStyle:{ color:"#ECEEF2", // 设置线条喂风格为虚线 type:"dashed" } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, } ], // 数据可以通过xAxisIndex,yAxisIndex属性,来指定是哪个grid的数据 series: [ // 第一个图表的数据 { // 平滑曲线 smooth:true, // 是否显示折线上的圆点 symbol:'none', // 线条颜色 lineStyle:{ color:"#0481F8", width:1 }, xAxisIndex:0, yAxisIndex:0, data: grid1Data, type: 'line', }, // 第二个图表的数据 { xAxisIndex:1, yAxisIndex:1, // 柱状图柱子宽度 barWidth:1, data: grid2Data, type: 'bar', // 设置柱状图颜色 itemStyle:{ normal: { color: function (params) { return volumeColor1[params.dataIndex]; } } } }, // 第三个图表的数据 { // 平滑曲线 smooth:true, // 是否显示折线上的圆点 symbol:'none', // 线条颜色 lineStyle:{ color:"#0481F8", width:1 }, xAxisIndex:2, yAxisIndex:2, data: grid3Data, type: 'line', }, // 第四个图表的数据 { xAxisIndex:3, yAxisIndex:3, // 柱状图柱子宽度 barWidth:1, data: grid4Data, type: 'bar', // 设置柱状图颜色 itemStyle:{ normal: { color: function (params) { return volumeColor2[params.dataIndex]; } } } } ] }; chart.setOption(option); }
效果图如下:
我们的分时图已经基本完成了,但是我们还是能看到11:30/13:00这里有一段是没有连接上的,因为是两个图表的原因所以会断开,这个将在下一章修正。下一章我们将添加图表的一些指示器,修正一些小问题。