JS使用canvas技術(shù)模仿echarts柱狀圖
canvas 畫布是html5中新增的標(biāo)簽,可以通過js操作 canvas 繪圖 API在網(wǎng)頁中繪制圖像。
百度開發(fā)了一個開源的可視化圖表庫ECharts,功能非常強大,可以實現(xiàn)折線圖、柱狀圖、散點圖、餅圖、K線圖、地圖等多種圖表。很多項目都有使用過ECharts開發(fā)過圖表功能。
本實例教程使用原生js教你開發(fā)一個仿ECharts的柱狀圖。學(xué)習(xí)本教程之前,讀者需要具備html和css技能,同時需要有簡單的JavaScript基礎(chǔ)。
按照ECharts的開發(fā)方法,圖表都是生成在一個HTML元素中。所以本實例中也先準(zhǔn)備一個id名為canvasWrap的div元素,如下所示:
<div id='canvasWrap'></div>
然后在canvasWrap元素中創(chuàng)建canvas元素,再在canvas元素上繪制柱狀圖。開發(fā)之前,按照慣例,還是先分析柱狀圖的具體操作,再根據(jù)具體操作把實現(xiàn)功能的方法分成多個步驟,接下來一個步驟一個步驟去完成它。
1. 編寫柱狀圖數(shù)據(jù)2. 獲取canvasWrap元素及寬高3. 創(chuàng)建繪圖環(huán)境3.1 創(chuàng)建canvas畫布3.2 設(shè)置canvas畫布的寬度和高度3.3 將canvas畫布放入到canvasWrap元素中3.4 創(chuàng)建繪圖上下文環(huán)境4. 設(shè)定坐標(biāo)區(qū)域5. 繪制x軸5.1 繪制軸線5.2 繪制刻度線5.3 繪制刻度名稱6. 繪制y軸6.1 繪制軸線6.2 繪制刻度線6.3 繪制刻度值6.4 繪制x軸網(wǎng)格線7. 繪制柱圖7.1 計算柱圖寬度7.2 計算柱圖高度7.3 計算柱圖X起點7.4 計算柱圖Y起點7.5 繪制柱圖
具體代碼如下:
//1 編寫柱狀圖數(shù)據(jù)option = { //x軸數(shù)據(jù) xAxis: { data: [’Mon’, ’Tue’, ’Wed’, ’Thu’, ’Fri’, ’Sat’, ’Sun’] }, //柱圖數(shù)據(jù) series: [{ //多寫幾組數(shù)據(jù),用于查看不同數(shù)據(jù)時的圖表效果 // data: [0.01, 0.2, 0.05, 0.07, 0.04, 0.13, 0.9], // data: [1, 1, 5, 7, 4, 1, 9], // data: [1213, 30, 150, 80, 70, 910, 630], data: [120, 199, 150, 180, 70, 110, 130], //圖形樣式:柱圖 type: ’bar’ }]};//創(chuàng)建圖表函數(shù),wrap:圖表父元素id;data:圖表數(shù)據(jù)function fnCharts(wrap,data){ //2.獲取canvasWrap元素 var eWrap = document.getElementById(wrap); //2.獲取canvasWrap元素寬度和高度,用于設(shè)置canvas畫布大小 var nWrapW = eWrap.offsetWidth; var nWrapH = eWrap.offsetHeight; //3.1 創(chuàng)建canvas畫布 var eCanvas = document.createElement(’canvas’); //3.2 設(shè)置canvas畫布的寬度和高度 eCanvas.width = nWrapW; eCanvas.height = nWrapH; //3.3 將canvas畫布放入到canvasWrap元素中 eWrap.appendChild(eCanvas); //3.4 創(chuàng)建繪圖上下文環(huán)境(才能夠在Canvas畫布上繪制) var oCtx = eCanvas.getContext(’2d’); //4.設(shè)定坐標(biāo)區(qū)域左上角和右下角 //起點設(shè)置為50.5,而不是整數(shù),是為了讓線條變清晰 var nZoneStartX = 50.5; var nZoneStartY = 50.5; var nZoneEndX = nWrapW - nZoneStartX; var nZoneEndY = nWrapH - nZoneStartY; //5.1 使用線條函數(shù)繪制x軸軸線 fnCreatLine(nZoneStartX,nZoneEndY,nZoneEndX,nZoneEndY); //計算x軸長度 var nLonX = nZoneEndX - nZoneStartX; //獲取x軸數(shù)據(jù)數(shù)組長度 var nDataLon = option.xAxis.data.length; //根據(jù)x軸數(shù)據(jù)數(shù)組長度循環(huán),在循環(huán)中繪制刻度線和刻度數(shù)值名稱 for(let i=0;i<nDataLon;i++){ //計算出x軸刻度線起點在x軸上的值 let nScaleX = nZoneStartX+Math.floor(nLonX*(i/nDataLon)); //刻度線起點都在x軸上 let nScaleY = nZoneEndY; //5.2 繪制刻度線,長度為10 fnCreatLine(nScaleX,nScaleY,nScaleX,nScaleY+10); //從數(shù)據(jù)中獲取刻度名稱字符串 let sName = option.xAxis.data[i]; //計算出刻度名稱起點 let nNameX = nZoneStartX+Math.floor(nLonX*(i/nDataLon))+Math.floor(nLonX*(1/nDataLon))/2; let nNameY = nZoneEndY+15; //5.3 繪制刻度名稱 fnCreatText(sName,nNameX,nNameY,’#aaa’,’center’); } //6.1 使用線條函數(shù)繪制y軸軸線 fnCreatLine(nZoneStartX,nZoneEndY,nZoneStartX,nZoneStartY); //繪制y軸刻度線前,需要有刻度最大值、最小值、刻度線段數(shù)和刻度線之間的間隔這些數(shù)據(jù)。 //刻度最大值先從數(shù)組中取最大值,等下再計算應(yīng)該顯示的最大值 var nMaxScal = Math.max.apply(null,option.series[0].data); //刻度最小值在本實例中取0 var nMinScal = 0; //刻度線段數(shù)在本實例中設(shè)置為4 var nSplit = 4; //計算刻度間隔值 var nStep = (nMaxScal-nMinScal)/nSplit; //這時候會發(fā)現(xiàn)刻度間隔值好像有點奇怪,因為一般圖表的刻度間隔值都是5的倍數(shù), //比如:[0,0.5,1.0,1.5,2]或[0,50,100,150,200]。 //所以還需要進一步計算,看nStep是否是5的倍數(shù),如果不是,則遞增nIncrease,使其達到最接近的5的倍數(shù)。 //計算第一步,根據(jù)nStep算出倍數(shù)值應(yīng)該是0.5或5或50或... //在本實例中通過把nStep數(shù)值先轉(zhuǎn)換為字符串再進行處理(也可以使用對數(shù)和指數(shù)去計算)。 var sTemp = ’’ + nStep; //把nStep轉(zhuǎn)換為字符串 //聲明一個需要遞增的數(shù),默認為1 var nIncrease = 1; //聲明一個變量用于解決小數(shù)相乘產(chǎn)生的精度bug var nTempMultiple = 1; //nIncrease取10的n次冪,通過以下判斷計算 if(sTemp.indexOf(’.’)==-1){ //如果nStep不包含小數(shù)點,nIncrease取10的sTemp.length-2次冪。 //比如nStep為19的話,nIncrease = 10的0次冪,遞增數(shù)為1 //nStep為9的話,nIncrease = 10的-1次冪,遞增數(shù)為0.1 //nStep為199的話,nIncrease = 10的1次冪,遞增數(shù)為10 nIncrease = Math.pow(10,sTemp.length-2); }else{ //如果nStep包含小數(shù)點,nIncrease取10的sTemp整數(shù)位-2次冪。 nIncrease = Math.pow(10,sTemp.indexOf(’.’)-2); //這個變量用于解決小數(shù)相乘可能產(chǎn)生的精度bug,比如nIncrease是小數(shù)的情況 nTempMultiple = Math.pow(10,sTemp.indexOf(’.’)); } //倍數(shù)取整,便于遞增,如165改成160,16.5改成16,1.65改成1.6,可通過下列公式實現(xiàn) nStep = Math.ceil(nStep/nIncrease)*(nIncrease*nTempMultiple)/nTempMultiple; //使用循環(huán)遞增nIncrease修正刻度值 while(nStep%(nIncrease*5)!=0){ nStep += nIncrease*1; } //通過間隔值乘以線段數(shù),修改刻度最大值 nMaxScal = nStep * nSplit; //計算y軸長度,這里多減3是因為y軸頂端要留點距離 var nLonY = nZoneEndY - nZoneStartY - 3; //繪制y軸刻度 for(let i=0;i<=nSplit;i++){ //刻度線起點都在y軸上 let nScaleX = nZoneStartX; //計算出y軸刻度線起點在y軸上的值 let nScaleY = nZoneEndY-Math.floor(nLonY*(i/nSplit)); //6.2 繪制刻度線 fnCreatLine(nScaleX,nScaleY,nScaleX-10,nScaleY); //6.3 繪制刻度值 fnCreatText(’’+i*nStep,nScaleX-20,nScaleY,’#333’); if(i!=0){ //6.4 非0位置,繪制x軸網(wǎng)格線 fnCreatLine(nScaleX,nScaleY,nScaleX+nLonX,nScaleY,’#ccc’); } } //7.1 計算柱圖寬度 let nBarWidth = Math.ceil(Math.floor(nLonX*(1/nDataLon))*.8); //遍歷x軸數(shù)據(jù) for(let i=0;i<nDataLon;i++){ //7.2 計算柱圖高度 let nBarHeight = nLonY/nMaxScal*option.series[0].data[i]; //7.3 計算柱圖X起點 let nBarStartX = nZoneStartX+Math.floor(nLonX*(i/nDataLon)) +(Math.floor(nLonX*(1/nDataLon))-nBarWidth)/2; //7.4 計算柱圖Y起點 let nBarStartY = nZoneEndY-nBarHeight; //7.5 繪制柱圖 fnCreatRect(nBarStartX,nBarStartY,nBarWidth,nBarHeight); } //繪制線條函數(shù) function fnCreatLine(sX,sY,eX,eY,color=’#000’){ //開始繪制路徑 oCtx.beginPath(); //設(shè)置路徑顏色 oCtx.strokeStyle = color; //設(shè)置路徑起點和終點,繪制線條 oCtx.moveTo(sX,sY); oCtx.lineTo(eX,eY); //給路徑添加顏色 oCtx.stroke(); } //繪制文字 function fnCreatText(text,x,y,color=’#000’,align=’end’,baseLine=’middle’){ //設(shè)置文字顏色 oCtx.fillStyle = color; //設(shè)置水平對齊方式 oCtx.textAlign = align; //設(shè)置垂直對齊方式 oCtx.textBaseline = baseLine; //繪制文字 oCtx.fillText(text,x,y); } //繪制矩形 function fnCreatRect(x,y,width,height,color=’#a00’){ //設(shè)置顏色 oCtx.fillStyle = color; oCtx.fillRect(x,y,width,height); }}//調(diào)用圖表函數(shù),并傳入元素id和option數(shù)據(jù)fnCharts(’canvasWrap’,option);
這篇實例教程可能需要點耐心去讀源碼,如果碰到不明白的地方,可以在不明白的源碼位置輸出值,也許能豁然開朗。
以上就是JS使用canvas技術(shù)模仿echarts柱狀圖的詳細內(nèi)容,更多關(guān)于JS使用canvas柱狀圖的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
