使用百度echarts仿雪球分时图(四)
这章节来收拾一下一些小BUG,顺便把各个小提示信息也补上,分时图也就完成了。
上章节末尾提到的一个bug,就是第一个grid跟第三个grid之间是断开的,折线并没有连在一起,所以先来收拾这个问题。没有连着一起的原因主要是第一个grid的最后一条数据的值,跟第三个grid的第一条数据不一样,而且grid之间是不会帮你把这两个点连接起来的,所以会出现一上一下的断崖。知道了是什么原因,那就知道该怎么办了。在遍历数据的时候,我们先往grid3Data数据里加一条数据,这条数据的值是grid1Data的最后一条数据,也就是11:30的那条数据,这样第一个grid的最后一条数据跟第三个grid的第一条数据就一样了,这样就可以连起来了。但是还会有个问题,因为第三个grid跟第四个grid的x轴类型是 ‘time’类型,但是第一条数据是11:30,第二条数据是13:00,如果继续使用'time'类型的话,第三个grid会有很长一条直线,就是11:30到13:00之间这一段会很平的,所以第三第四个grid不能使用'time'类型了,改用'category'类目类型。改用'category'类型后,要新建两个数组来存放x轴的时间,分别是grid3DateData,grid4DateData,然后grid3Data和grid4Data存放的不再是数组,而是数值。
遍历数据的代码稍微修改一下。下午数据的部分修改为
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; } // 第三grid的数据先添加一条数据 if(grid3Data.length == 0){ grid3Data.push(data.data.items[i-1].current); grid3DateData.push(data.data.items[i-1].timestamp); } // 右上方折线图 grid3Data.push(data.data.items[i].current); grid3DateData.push(data.data.items[i].timestamp); 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); } // 第四grid的数据先添加一条数据 if(grid4Data.length == 0){ grid4Data.push(data.data.items[i-1].volume); grid4DateData.push(data.data.items[i-1].timestamp); } // 右下方柱状图 grid4Data.push(data.data.items[i].volume); grid4DateData.push(data.data.items[i].timestamp);
然后第三第四个grid的x 轴配置中,data分别设置成grid3DateData和grid4DateData。然后又出现新的问题,x轴的坐标轴线分布不均匀,因为我们用的是 category 的类型,而且我们x轴的数据很多,echarts为了保证能够显示足够的x轴的文字,所以会出现跳跃性的线条,显得分布不均匀。还好,echarts中是有属性可以自定义去显示哪条x轴坐标轴线。就是xAxis.splitLine.interval这个属性,可以设置数字,也可以是回调方法,interval这个属性很熟悉了,就是线条的间隔,所以接下来使用回调方法的方式来显示需要显示的线条。第三第四个grid的xAxis.splitLine.interval属性都是这样写的
interval:function(index,value){ // 第一条第二条线是不需要显示的,第一条是11:30的,第一个grid已经有这条数据了,所以不需要显示 // 第二条显示的话,在中间部分会出现2条线,所以也不要显示 if(index == 0 || index == 1){ return false; } // 这里的意思是第一条数据后,每30分钟显示一条线 if((index - 1) % 30 == 0){ return true; } return false; }
这么一改,不连接的问题就解决了。
接下来把各个提示信息调整出来。首先要把指示器axisPointer这个属性配置一下,axisPointer有分为公共配置和各个坐标轴的单独配置。公共配置是配置一些公共的属性,各个坐标轴的配置则是配置不同坐标轴的样式,以及文字的格式化。这里我们两者都需要进行配置。
公共配置部分
axisPointer.show默认是false的,我们设置成true,指示器就出来了,但是会发现,线条也是断开的,只会显示其中的一个grid的线条,这里需要配置一下axisPointer.link属性来联动。link属性的介绍:不同轴的 axisPointer 可以进行联动,在这里设置。联动表示轴能同步一起活动。轴依据他们的 axisPointer 当前对应的值来联动,link 是一个数组,其中每一项表示一个 link group,一个 group 中的坐标轴互相联动。这里我们属性link的xAxisIndex和yAxisIndex属性,这两个属性是数组类型,里面值是数值,也就是grid的下标。重新看一下分时图,我们放鼠标在左上方的的区域,我们希望的是x轴是第一个第二个grid联动,y轴则是第一个第三个联动;放在右上方边的区域的话,x轴是第三个第四个grid联动,y轴还是第一个第三个联动。以此类推出左下方右下方的联动,得到的代码是这样的
axisPointer:{ show:true, // 配置线条风格为虚线风格 lineStyle:{ type:'dashed' }, link:[ { xAxisIndex:[0,1], },{ xAxisIndex:[2,3], },{ yAxisIndex:[0,2] },{ yAxisIndex:[1,3] } ] },
再看一下雪球的分时图,发现分时图有指示器的地方分别是第一个第二个第三个grid的y轴,第二个第四个grid的x轴,那么接下来要单独配置一下各个坐标轴的指示器。
所有指示器的show属性都设置成true,显示与否则通过label.show属性来控制。第一个第二个第三个grid的y轴的指示器的 label.show属性我们都设置成true,第四个则设置成false。第二个第四个grid的x轴的指示器的label.show属性设置成true,第一个第三个grid的x轴的指示器设置成false。设置完成后,成功的只显示了需要显示的指示器,但是默认的样式却不是我们想看到的样式。接下来美化一下
1.字体太大了,把label.fontSize调成10。
2.内边距太大了,把label.padding调成2即可。
3.阴影效果也不需要了,把label.shadowBlur调成0。
4.字体颜色也改成深色的,设置label.color即可。
经过调整后,样式已经达到我们的要求,但是指示器上的文字还没达到要求,那么就使用label.formatter的回调函数进行格式化,不同的坐标轴的格式化方式不一样,这里不花太大篇幅去讲解。
第一个第三个grid的x轴指示器配置
axisPointer:{ show:true, label:{ show:false } }
第一个grid的y轴指示器配置
axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, margin:-44, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ return data.value.toFixed(2); } }, }
第三个grid的y轴指示器配置
axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, margin:-34, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ var persent = (data.value - lastPrice) / lastPrice; persent = (persent < 0) ? persent * -1 : persent; persent = persent * 100; return persent.toFixed(2) + '%'; } }, }
第二个grid的x轴指示器配置
axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, margin:0, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ return echarts.format.formatTime('hh:mm', parseFloat(data.value)) } }, }
第二个grid的y轴指示器配置
axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ if(data.value > 1000000){ return parseFloat(data.value / 1000000).toFixed(2)+"万手"; } return data.value; } }, }
第四个grid的x轴指示器配置
axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, margin:0, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ return echarts.format.formatTime('hh:mm', parseFloat(data.value)); } }, }
第四个grid的y轴指示器配置
axisPointer:{ show:true, label:{ show:false } }
到这里,所有坐标轴的指示器都设置完成了,效果也如预期那样。最后还有一个悬浮的提示框需要配置,配置完后我们的分时图就完成了。
配置悬浮的提示框主要是配置根属性的tooltip属性。tooltip有个一个trigger的触发类型属性,item:数据项图形触发,主要在散点图,饼图等无类目轴的图表中使用;axis:坐标轴触发,主要在柱状图,折线图等会使用类目轴的图表中使用;none:什么都不触发。因为默认是item,这里把触发类型改成 axis ,提示框就会出来了。但是提示框的位置还有样式,都不是我们想要的,又要再给它美化一下。首先我们先对它的位置进行固定,使用position属性进行配置,position可以是字符串,数组,回调函数,要高度自定义的话,显然回调函数比较合适,回调函数返回一个对象,这个对象与CSS的position属性类型,对象里面是使用top,right,bottom,left属性来控制位置的,我们可以根据回调函数里的5个回调参数来进行相应的配置,这5个回调参数分别是:
point: 鼠标位置,如 [20, 40]。
params: 同 formatter 的参数相同。
dom: tooltip 的 dom 对象。
rect: 只有鼠标在图形上时有效,是一个用x
, y
, width
, height
四个属性表达的图形包围盒。
size: 包括 dom 的尺寸和 echarts 容器的当前尺寸,例如:{contentSize: [width, height], viewSize: [width, height]}
。
point属性是鼠标当前的x值和y值;params跟formatter的参数相同,但是我们不需要格式化的相关配置,所以这个可以不用;dom对象,这个先保留;rect这个属性是鼠标放在提示框上才会有效,否则就是undefined,要想鼠标能放到提示框上,要配置enterable属性为true,但是我们不需要放鼠标上去,所以不需要;size属性,里面的contentSize属性指的是提示框的宽高,viewSize则是整个图表的大小,我们需要size的viewSize结合point鼠标位置属性来对提示框进行一个位置的定位。
我们要做的效果是鼠标在左边图表的时候,提示框在右边,鼠标在右侧图表的的时候,提示框在左边,那么就要对返回对象的left值或者right值进行配置了。判断鼠标是在左边还是在右边很简单,只需要判断鼠标的x值是否大于第一个第二个grid的宽度即可,因为左右两边的宽度是相等的,所以第一个第二个grid的宽度就是viewSize[width] / 2,只要鼠标位置的x值大于这个值,那就是鼠标在右边,小于这个值那就是在左边;同时我们要固定在头部,所以要把top值也设置一下。
position:function(point, params, dom, rect, size){ var obj ={ top:10 } if(point[0] > size.viewSize[0] / 2){ obj["left"] = 70; }else{ obj["right"] = 70; } return obj; }
位置调整好后,开始整理样式。
1.文字的颜色通过textStyle.color属性设置成黑色
2.设置边框,borderWidth:1,borderColor:'#ECEEF2'
3.设置背景色为白色半透明,backgroundColor:'rgba(255,255,255,0.9)'
4.动画效果也不要了,直接设置动画时长为0,transitionDuration:0
样式调整好后,提示框的文字也需要进行一次格式化,在formatter的回调函数中进行配置。因为提示框的渲染方式renderMode,默认是html,所以回调函数里面我们可以配合CSS来编写我们自己的html。formatter的回调参数有3个,分别是:
1.params,这是提示框里面展示的数据的数据集。
2.ticket,这是异步回调的标识,数据格式跟params是一样的,一般配合第三个参数callback使用
3.callback,异步回调,与第二个参数ticket配合进行异步的数据展示。例如提示框中需要进行数据获取的请求时,可以用到这个组合。
这里我们不需要异步请求数据,所以直接使用params的数据进行展示。首先我们要遍历params数据集,拿到里面的数据,再通过数据集的数据名称seriesName进行判断是什么数据,对数据进行格式化,最后拼接成一段html用于渲染。(注:seriesName可以在series中,对数据进行命名)
命名数据
// 第一个图表的数据 { name:"最新1", // 平滑曲线 smooth:true, // 是否显示折线上的圆点 symbol:'none', // 线条颜色 lineStyle:{ color:"#0481F8", width:1 }, xAxisIndex:0, yAxisIndex:0, data: grid1Data, type: 'line', z:3, areaStyle:{ color:"#F8FAFF" } },
formatter回调函数
formatter:function(params,ticket,callback){ var html = ''; var x,j,c; for(var d in params){ if(params[d].seriesName == '成交量1' || params[d].seriesName == '成交量2' ){ c = params[d]; } if(params[d].seriesName == '最新1' || params[d].seriesName == '最新2' ){ x = params[d]; } } if(!c.axisValue){ return; } html += '<div class="tooltips-item"><span class="name">时间</span><span class="value">'+ echarts.format.formatTime('MM-dd hh:mm',parseFloat(c.axisValue)) +'</span></div>'; html += '<div class="tooltips-item"><span class="name">最新</span><span class="value '+ colorCls((typeof x.data == 'number' ? x.data : x.data[1])) +'">'+ (typeof x.data == 'number' ? x.data : x.data[1]) +'</span></div>'; html += '<div class="tooltips-item"><span class="name">涨跌幅</span><span class="value '+ colorCls((typeof x.data == 'number' ? x.data : x.data[1])) +'">'+ ((((typeof x.data == 'number' ? x.data : x.data[1]) - lastPrice)/ lastPrice * 100).toFixed(2)) +'%</span></div>'; html += '<div class="tooltips-item"><span class="name">涨跌额</span><span class="value '+ colorCls((typeof x.data == 'number' ? x.data : x.data[1])) +'">'+ ((typeof x.data == 'number' ? x.data : x.data[1])- lastPrice).toFixed(2) +'</span></div>'; html += '<div class="tooltips-item"><span class="name">成交量</span><span class="value">'+ (typeof c.data == 'number' ? (c.data / 1000000).toFixed(2) : (c.data[1] / 1000000).toFixed(2)) +'万手</span></div>'; return html; }
部分CSS
#charts{ /*折线图的宽度*/ width:640px; /*折线图的高度*/ height:390px; } .tooltips-item{ display:flex; display:-webkit-flex; justify-content: space-between; color:#33333c; font-size:10px; width:120px; } .green{ color:#009933 } .red{ color:#E24528 }
到这里,提示框的配置也完成了。仿分时图系列就结束了。
最终效果图如下
完整的代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>分时图</title> <script type="text/javascript" src="js/jQuery.js" ></script> <script type="text/javascript" src="js/echarts.min.js" ></script> <style> #charts{ /*折线图的宽度*/ width:640px; /*折线图的高度*/ height:390px; } .tooltips-item{ display:flex; display:-webkit-flex; justify-content: space-between; color:#33333c; font-size:10px; width:120px; } .green{ color:#009933 } .red{ color:#E24528 } </style> </head> <body> <div id="charts"> </div> <script type="text/javascript"> // 雪球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 = []; var grid3DateData = []; // 第四个grid数据(柱状图) var grid4Data = []; var grid4DateData = []; // 柱状图的颜色 // 柱状图的红绿规则比较麻烦,所以本次采用的规则则是根据价格的涨跌来区分 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 colorCls(num){ if(num > lastPrice){ return "red"; }else if(num == lastPrice){ return ""; } return "green"; } 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; } // 第三grid的数据先添加一条数据 if(grid3Data.length == 0){ grid3Data.push(data.data.items[i-1].current); grid3DateData.push(data.data.items[i-1].timestamp); } // 右上方折线图 grid3Data.push(data.data.items[i].current); grid3DateData.push(data.data.items[i].timestamp); 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); } // 第四grid的数据先添加一条数据 if(grid4Data.length == 0){ grid4Data.push(data.data.items[i-1].volume); grid4DateData.push(data.data.items[i-1].timestamp); } // 右下方柱状图 grid4Data.push(data.data.items[i].volume); grid4DateData.push(data.data.items[i].timestamp); } } // 重新计算价格的最大最小值,以达到对称的效果 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(){ var nowDate; // 初始化一个echarts的对象 var chart = echarts.init(document.getElementById('charts')); // echarts折线图的配置项 var option = { animation:false, //坐标轴指示器 axisPointer:{ show:true, // 配置线条风格为虚线风格 lineStyle:{ type:'dashed' }, link:[ { xAxisIndex:[0,1], },{ xAxisIndex:[2,3], },{ yAxisIndex:[0,2] },{ yAxisIndex:[1,3] } ] }, // 悬浮框 tooltip:{ trigger:'axis', position:function(point, params, dom, rect, size){ var obj ={ top:10 } console.log(size); if(point[0] > size.viewSize[0] / 2){ obj["left"] = 70; }else{ obj["right"] = 70; } return obj; }, formatter:function(params,ticket,callback){ var html = ''; var x,j,c; for(var d in params){ if(params[d].seriesName == '成交量1' || params[d].seriesName == '成交量2' ){ c = params[d]; } if(params[d].seriesName == '最新1' || params[d].seriesName == '最新2' ){ x = params[d]; } } if(!c.axisValue){ return; } html += '<div class="tooltips-item"><span class="name">时间</span><span class="value">'+ echarts.format.formatTime('MM-dd hh:mm',parseFloat(c.axisValue)) +'</span></div>'; html += '<div class="tooltips-item"><span class="name">最新</span><span class="value '+ colorCls((typeof x.data == 'number' ? x.data : x.data[1])) +'">'+ (typeof x.data == 'number' ? x.data : x.data[1]) +'</span></div>'; html += '<div class="tooltips-item"><span class="name">涨跌幅</span><span class="value '+ colorCls((typeof x.data == 'number' ? x.data : x.data[1])) +'">'+ ((((typeof x.data == 'number' ? x.data : x.data[1]) - lastPrice)/ lastPrice * 100).toFixed(2)) +'%</span></div>'; html += '<div class="tooltips-item"><span class="name">涨跌额</span><span class="value '+ colorCls((typeof x.data == 'number' ? x.data : x.data[1])) +'">'+ ((typeof x.data == 'number' ? x.data : x.data[1])- lastPrice).toFixed(2) +'</span></div>'; html += '<div class="tooltips-item"><span class="name">成交量</span><span class="value">'+ (typeof c.data == 'number' ? (c.data / 1000000).toFixed(2) : (c.data[1] / 1000000).toFixed(2)) +'万手</span></div>'; return html; }, textStyle:{ color:"#000" }, borderWidth:1, borderColor:"#ECEEF2", backgroundColor:"rgba(255,255,255,0.9)", transitionDuration:0, axisPointer:{ animation:false, type:"cross" } }, // grid grid:[ // 第一个grid { top:10,// 图表的外边距 height:240,// 图表的高度 left:'5%', width:'45%',//因为是左右各一个图表,使用百分比的方式显得更方便, }, // 第二个grid,第二个图表是在第一个图表的下方,所以要把它定位到底部 { top:280,//设置上方的外边距是第一个图表的高度再加10,使用top是方便我们调整下方grid的高度 left:'5%', width:'45%',// 宽度与第一个图表一个大 height:80, }, // 第三个grid,第三个图表是在第一个图表的右方,所以要把它定位到右方 { top:10,// 图表的外边距 left:'50%',//设置右边图表的左边距是第一个图表的大小,达到定位右边的效果 width:'45%',// 宽度与第一个图表一个大 height:240, }, // 第四个grid,第四个图表是在第三个图表的下方,所以要把它定位到底部 { top:280,//设置上方的外边距是第三个图表的高度再加10,使用top是方便我们调整下方grid的高度 left:'50%',//设置右边图表的左边距是第三个图表的大小,达到定位右边的效果 width:'45%',// 宽度与第一个图表一个大 height:80, } ], // 多个图表则会存在对个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', axisPointer:{ show:true, label:{ show:false } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, splitLine:{ lineStyle:{ color:"#ECEEF2", type:"solid" } }, }, // 第二个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 "11:30/13:00"; } if(a == "09:30"){ return " 09:30"; } return a; } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, splitLine:{ lineStyle:{ color:"#ECEEF2", type:"solid" } }, axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, margin:0, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ return echarts.format.formatTime('hh:mm', parseFloat(data.value)) } }, }, }, // 第三个grid的x轴属性 { // 告诉echarts,这个第一个grid的x轴 gridIndex:2, // 坐标轴是否留白 boundaryGap:false, // x轴的刻度 axisTick:{show:false}, // x轴的刻度值 axisLabel:{show:false}, type: 'category', data:grid3DateData, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, max:"dataMax", min:"dataMin", axisPointer:{ show:true, label:{ show:false } }, splitLine:{ show:true, lineStyle:{ color:"#ECEEF2", type:"solid" }, interval:function(index,value){ if(index == 0 || index == 1){ return false; } if((index - 1) % 30 == 0){ return true; } return false; }, }, }, // 第四个grid的x轴属性 { // 告诉echarts,这个第一个grid的x轴 gridIndex:3, // 坐标轴是否留白 boundaryGap:false, // x轴的刻度 axisTick:{ show:false }, type: 'category', max:"dataMax", min:"dataMin", data:grid4DateData, axisLabel: { fontSize:12, show: true, showMinLabel:false, color:'#888', interval:function(index,value){ if((index - 1) % 30 == 0){ return true; } return false; }, formatter: function (value) { var a = echarts.format.formatTime('hh:mm', parseFloat(value)); if(a == "15:00"){ return "15:00 "; } return a; } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, splitLine:{ show:true, lineStyle:{ color:"#ECEEF2", type:"solid" }, interval:function(index,value){ // 第一条第二条线是不需要显示的,第一条是11:30的,第一个grid已经有这条数据了,所以不需要显示 // 第二条显示的话,在中间部分会出现2条线,所以也不要显示 if(index == 0 || index == 1){ return false; } // 这里的意思是第一条数据后,每30分钟显示一条线 if((index - 1) % 30 == 0){ return true; } return false; }, }, axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, margin:0, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ return echarts.format.formatTime('hh:mm', parseFloat(data.value)); } }, }, } ], // y轴配置 yAxis: [ // 第一个grid的y轴属性 { // 去掉刻度值旁边的指示线 axisTick:{show:false}, splitNumber:9, gridIndex:0, interval:priceInterval, max:priceMax, min:priceMin, splitLine:{ lineStyle:{ color:"#ECEEF2", type:"solid", } }, 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; } } }, z:3, axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, margin:-44, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ return data.value.toFixed(2); } }, }, }, // 第二个grid的y轴属性 { // 去掉刻度值旁边的指示线 axisTick:{show:false}, splitNumber:3, gridIndex:1, interval:volumeInterval, max:volumeMax, min:0, splitLine:{ lineStyle:{ color:"#ECEEF2", type:"solid" } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ if(data.value > 1000000){ return parseFloat(data.value / 1000000).toFixed(2)+"万手"; } return data.value; } }, }, axisLabel:{ align:"left", verticalAlign:"top", //设置显示坐标轴的数值为不显示 show:true, fontSize:10, margin:0, showMaxLabel:true, showMinLabel:false, color:"#33353C", formatter: function (value, index) { // 格式化成月/日,只在第一个刻度显示年份 if(value == volumeMax){ // 方便演示 if(value > 1000000){ value = parseFloat(value / 1000000).toFixed(2)+"万手" } return value; } return ""; } }, }, // 第三个grid的y轴属性 { // 去掉刻度值旁边的指示线 axisTick:{show:false}, splitNumber:9, position:'right', gridIndex:2, interval:priceInterval, max:priceMax, min:priceMin, splitLine:{ lineStyle:{ color:"#ECEEF2", type:"solid" } }, 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; } } }, z:3, axisPointer:{ show:true, type:"line", label:{ show:true, fontSize:10, margin:-34, padding:2, shadowBlur:0, color:"#33353C", formatter:function(data){ var persent = (data.value - lastPrice) / lastPrice; persent = (persent < 0) ? persent * -1 : persent; persent = persent * 100; return persent.toFixed(2) + '%'; } }, }, }, // 第四个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:"solid" } }, axisPointer:{ show:true, label:{ show:false } }, axisLine:{ lineStyle:{ color:"#ECEEF2" } }, } ], // 数据可以通过xAxisIndex,yAxisIndex属性,来指定是哪个grid的数据 series: [ // 第一个图表的数据 { name:"最新1", // 平滑曲线 smooth:true, // 是否显示折线上的圆点 symbol:'none', // 线条颜色 lineStyle:{ color:"#0481F8", width:1 }, xAxisIndex:0, yAxisIndex:0, data: grid1Data, type: 'line', z:3, areaStyle:{ color:"#F8FAFF" } }, // 第二个图表的数据 { name:"成交量1", xAxisIndex:1, yAxisIndex:1, // 柱状图柱子宽度 barWidth:1, data: grid2Data, type: 'bar', // 设置柱状图颜色 itemStyle:{ normal: { color: function (params) { return volumeColor1[params.dataIndex]; } } } }, // 第三个图表的数据 { name:"最新2", // 平滑曲线 smooth:true, // 是否显示折线上的圆点 symbol:'none', // 线条颜色 lineStyle:{ color:"#0481F8", width:1 }, z:3, xAxisIndex:2, yAxisIndex:2, data: grid3Data, type: 'line', areaStyle:{ color:"#F8FAFF" } }, // 第四个图表的数据 { name:"成交量2", xAxisIndex:3, yAxisIndex:3, // 柱状图柱子宽度 barWidth:1, data: grid4Data, type: 'bar', // 设置柱状图颜色 itemStyle:{ normal: { color: function (params) { return volumeColor2[params.dataIndex]; } } } } ] }; chart.setOption(option); } </script> </body> </html>
总结:一开始做分时图的时候我也不懂从哪开始做,慢慢观察后才发现其实可以由折线图跟柱状图来拼接而成。知道拼接可以达到效果后,就开始写代码,期间也是遇到了很多问题,最开始是用两个echarts来组合,发现效果很丑,去查了一下文档,发现可以用grid来解决,接着就拆成了上下两个grid;然后上下午时间段这个问题,由于不是连续的,所以一个折线图解决不了,再次拆图表,把上下方的图表再一拆为二,拆成了两个后折线图出现了断崖式的效果,经过很多尝试后,把x轴的type修改成category属性,再对下午数据进行一些修改才解决了这个问题。做复杂的图表时,可以先分析一下这个图表可以由哪些图表来组成,然后再根据需求来对各个图表进行修改。echarts这个框架还是很好用的,图表类型丰富,文档全,能够高度自定义,重要还是免费的...