這章節將完成我們的分時圖,並使用真實的數據來進行展示分時圖。
一天的交易時間段分為上午的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這里有一段是沒有連接上的,因為是兩個圖表的原因所以會斷開,這個將在下一章修正。下一章我們將添加圖表的一些指示器,修正一些小問題。