JS使用canvas技術(shù)模仿echarts柱狀圖
canvas 畫布是html5中新增的標(biāo)簽,可以通過(guò)js操作 canvas 繪圖 API在網(wǎng)頁(yè)中繪制圖像。
百度開發(fā)了一個(gè)開源的可視化圖表庫(kù)ECharts,功能非常強(qiáng)大,可以實(shí)現(xiàn)折線圖、柱狀圖、散點(diǎn)圖、餅圖、K線圖、地圖等多種圖表。很多項(xiàng)目都有使用過(guò)ECharts開發(fā)過(guò)圖表功能。
本實(shí)例教程使用原生js教你開發(fā)一個(gè)仿ECharts的柱狀圖。學(xué)習(xí)本教程之前,讀者需要具備html和css技能,同時(shí)需要有簡(jiǎn)單的JavaScript基礎(chǔ)。
按照ECharts的開發(fā)方法,圖表都是生成在一個(gè)HTML元素中。所以本實(shí)例中也先準(zhǔn)備一個(gè)id名為canvasWrap的div元素,如下所示:
<div id='canvasWrap'></div>
然后在canvasWrap元素中創(chuàng)建canvas元素,再在canvas元素上繪制柱狀圖。開發(fā)之前,按照慣例,還是先分析柱狀圖的具體操作,再根據(jù)具體操作把實(shí)現(xiàn)功能的方法分成多個(gè)步驟,接下來(lái)一個(gè)步驟一個(gè)步驟去完成它。
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 計(jì)算柱圖寬度7.2 計(jì)算柱圖高度7.3 計(jì)算柱圖X起點(diǎn)7.4 計(jì)算柱圖Y起點(diǎn)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ù)時(shí)的圖表效果 // 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ū)域左上角和右下角 //起點(diǎn)設(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); //計(jì)算x軸長(zhǎng)度 var nLonX = nZoneEndX - nZoneStartX; //獲取x軸數(shù)據(jù)數(shù)組長(zhǎng)度 var nDataLon = option.xAxis.data.length; //根據(jù)x軸數(shù)據(jù)數(shù)組長(zhǎng)度循環(huán),在循環(huán)中繪制刻度線和刻度數(shù)值名稱 for(let i=0;i<nDataLon;i++){ //計(jì)算出x軸刻度線起點(diǎn)在x軸上的值 let nScaleX = nZoneStartX+Math.floor(nLonX*(i/nDataLon)); //刻度線起點(diǎn)都在x軸上 let nScaleY = nZoneEndY; //5.2 繪制刻度線,長(zhǎng)度為10 fnCreatLine(nScaleX,nScaleY,nScaleX,nScaleY+10); //從數(shù)據(jù)中獲取刻度名稱字符串 let sName = option.xAxis.data[i]; //計(jì)算出刻度名稱起點(diǎn) 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ù)組中取最大值,等下再計(jì)算應(yīng)該顯示的最大值 var nMaxScal = Math.max.apply(null,option.series[0].data); //刻度最小值在本實(shí)例中取0 var nMinScal = 0; //刻度線段數(shù)在本實(shí)例中設(shè)置為4 var nSplit = 4; //計(jì)算刻度間隔值 var nStep = (nMaxScal-nMinScal)/nSplit; //這時(shí)候會(huì)發(fā)現(xiàn)刻度間隔值好像有點(diǎn)奇怪,因?yàn)橐话銏D表的刻度間隔值都是5的倍數(shù), //比如:[0,0.5,1.0,1.5,2]或[0,50,100,150,200]。 //所以還需要進(jìn)一步計(jì)算,看nStep是否是5的倍數(shù),如果不是,則遞增nIncrease,使其達(dá)到最接近的5的倍數(shù)。 //計(jì)算第一步,根據(jù)nStep算出倍數(shù)值應(yīng)該是0.5或5或50或... //在本實(shí)例中通過(guò)把nStep數(shù)值先轉(zhuǎn)換為字符串再進(jìn)行處理(也可以使用對(duì)數(shù)和指數(shù)去計(jì)算)。 var sTemp = ’’ + nStep; //把nStep轉(zhuǎn)換為字符串 //聲明一個(gè)需要遞增的數(shù),默認(rèn)為1 var nIncrease = 1; //聲明一個(gè)變量用于解決小數(shù)相乘產(chǎn)生的精度bug var nTempMultiple = 1; //nIncrease取10的n次冪,通過(guò)以下判斷計(jì)算 if(sTemp.indexOf(’.’)==-1){ //如果nStep不包含小數(shù)點(diǎn),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ù)點(diǎn),nIncrease取10的sTemp整數(shù)位-2次冪。 nIncrease = Math.pow(10,sTemp.indexOf(’.’)-2); //這個(gè)變量用于解決小數(shù)相乘可能產(chǎn)生的精度bug,比如nIncrease是小數(shù)的情況 nTempMultiple = Math.pow(10,sTemp.indexOf(’.’)); } //倍數(shù)取整,便于遞增,如165改成160,16.5改成16,1.65改成1.6,可通過(guò)下列公式實(shí)現(xiàn) nStep = Math.ceil(nStep/nIncrease)*(nIncrease*nTempMultiple)/nTempMultiple; //使用循環(huán)遞增nIncrease修正刻度值 while(nStep%(nIncrease*5)!=0){ nStep += nIncrease*1; } //通過(guò)間隔值乘以線段數(shù),修改刻度最大值 nMaxScal = nStep * nSplit; //計(jì)算y軸長(zhǎng)度,這里多減3是因?yàn)閥軸頂端要留點(diǎn)距離 var nLonY = nZoneEndY - nZoneStartY - 3; //繪制y軸刻度 for(let i=0;i<=nSplit;i++){ //刻度線起點(diǎn)都在y軸上 let nScaleX = nZoneStartX; //計(jì)算出y軸刻度線起點(diǎn)在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 計(jì)算柱圖寬度 let nBarWidth = Math.ceil(Math.floor(nLonX*(1/nDataLon))*.8); //遍歷x軸數(shù)據(jù) for(let i=0;i<nDataLon;i++){ //7.2 計(jì)算柱圖高度 let nBarHeight = nLonY/nMaxScal*option.series[0].data[i]; //7.3 計(jì)算柱圖X起點(diǎn) let nBarStartX = nZoneStartX+Math.floor(nLonX*(i/nDataLon)) +(Math.floor(nLonX*(1/nDataLon))-nBarWidth)/2; //7.4 計(jì)算柱圖Y起點(diǎn) 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è)置路徑起點(diǎn)和終點(diǎn),繪制線條 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è)置水平對(duì)齊方式 oCtx.textAlign = align; //設(shè)置垂直對(duì)齊方式 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);
這篇實(shí)例教程可能需要點(diǎn)耐心去讀源碼,如果碰到不明白的地方,可以在不明白的源碼位置輸出值,也許能豁然開朗。
以上就是JS使用canvas技術(shù)模仿echarts柱狀圖的詳細(xì)內(nèi)容,更多關(guān)于JS使用canvas柱狀圖的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. ASP常用日期格式化函數(shù) FormatDate()2. Python 操作 MySQL數(shù)據(jù)庫(kù)3. Python數(shù)據(jù)相關(guān)系數(shù)矩陣和熱力圖輕松實(shí)現(xiàn)教程4. 開發(fā)效率翻倍的Web API使用技巧5. bootstrap select2 動(dòng)態(tài)從后臺(tái)Ajax動(dòng)態(tài)獲取數(shù)據(jù)的代碼6. CSS3中Transition屬性詳解以及示例分享7. js select支持手動(dòng)輸入功能實(shí)現(xiàn)代碼8. 什么是Python變量作用域9. vue使用moment如何將時(shí)間戳轉(zhuǎn)為標(biāo)準(zhǔn)日期時(shí)間格式10. python 如何在 Matplotlib 中繪制垂直線
