這章節來收拾一下一些小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這個框架還是很好用的,圖表類型豐富,文檔全,能夠高度自定義,重要還是免費的...