这章节将完成我们的分时图,并使用真实的数据来进行展示分时图。

一天的交易时间段分为上午的09:30~11:30,下午的13:00~15:00两个时间段,因为分时间段的关系,数据是不连续的,所以会先分为2个grid,上午的grid跟下午的grid,又因为分时图是由折线图跟柱状图来组成的,所以又把上下午的grid再一分为二,变成了4个grid,这也是为什么会分解成4个grid的原因。

先看看分时图的数据。

使用百度echarts仿雪球分时图(三)-LMLPHP

现在我们再重新看一下雪球的分时图

使用百度echarts仿雪球分时图(三)-LMLPHP

根路径的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); }

效果图如下:

使用百度echarts仿雪球分时图(三)-LMLPHP

我们的分时图已经基本完成了,但是我们还是能看到11:30/13:00这里有一段是没有连接上的,因为是两个图表的原因所以会断开,这个将在下一章修正。下一章我们将添加图表的一些指示器,修正一些小问题。

05-27 22:52