本章建議學習時間4小時javascript
學習方式:詳細閱讀,並手動實現相關代碼(若是沒有canvas基礎,須要先學習前面的canvas基礎筆記)html
學習目標:此教程將教會你們如何使用canvas繪製各類圖表,詳細分解步驟,本次講解餅狀圖。前端
演示地址: https://sutianbinde.github.io/charts/%E9%A5%BC%E7%8A%B6%E5%9B%BE-%E9%AB%98%E6%B8%85.htmljava
源文件下載地址:https://github.com/sutianbinde/chartsgit
餅狀圖github
餅狀圖是前端最基本的圖表之一,咱們的案例展現效果以下canvas
功能:圖表能夠根據數據自動變換比例,旋轉繪製的動畫,鼠標移入到對應模塊會實現顏色變化。瀏覽器
實現步驟學習
--新建Html文件,寫入canvas標籤,而且定義繪製圖表的方法(咱們js中的canvas寬高根據canvas父級標籤的寬高來設置,但願你們寫的時候必定給canvas添加父級div並指定寬高)字體
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> canvas{ border: 1px solid #A4E2F9; } </style> </head> <body> <div height="400" width="600" style="margin:50px"> <canvas id="chart"> 你的瀏覽器不支持HTML5 canvas </canvas> </div> <script type="text/javascript"> function goChart(dataArr){ } var chartData = [[50,"#2dc6c8","瓜子"], [100,"#b6a2dd", "花生"], [200,"#5ab1ee","土豆"], [700,"#d7797f","南瓜四號"]]; goChart(chartData); </script> </body> </html>
--在 goChart方法中定義須要使用的變量 並獲取 canvas上下文
// 聲明所需變量 var canvas,ctx; // 圖表屬性 var cWidth, cHeight, cMargin, cSpace; // 餅狀圖屬性 var radius,ox,oy;//半徑 圓心 var tWidth, tHeight;//圖例寬高 var posX, posY, textX, textY; var startAngle, endAngle; var totleNb; // 運動相關變量 var ctr, numctr, speed; //鼠標移動 var mousePosition = {}; //線條和文字 var lineStartAngle,line,textPadding,textMoveDis; // 得到canvas上下文 canvas = document.getElementById("chart"); if(canvas && canvas.getContext){ ctx = canvas.getContext("2d"); }
--初始化圖表(接着上一步的代碼寫在 goChart方法中 )
initChart(); // 圖表初始化 function initChart(){ // 圖表信息 cMargin = 20; cSpace = 40; canvas.width = canvas.parentNode.getAttribute("width")* 2 ; canvas.height = canvas.parentNode.getAttribute("height")* 2; canvas.style.height = canvas.height/2 + "px"; canvas.style.width = canvas.width/2 + "px"; cHeight = canvas.height - cMargin*2; cWidth = canvas.width - cMargin*2; //餅狀圖信息 radius = cHeight*2/6; //半徑 高度的2/6 ox = canvas.width/2 + cSpace; //圓心 oy = canvas.height/2; tWidth = 60; //圖例寬和高 tHeight = 20; posX = cMargin; posY = cMargin; // textX = posX + tWidth + 15 textY = posY + 18; startAngle = endAngle = 90*Math.PI/180; //起始弧度 結束弧度 rotateAngle = 0; //總體旋轉的弧度 //將傳入的數據轉化百分比 totleNb = 0; new_data_arr = []; for (var i = 0; i < dataArr.length; i++){ totleNb += dataArr[i][0]; } for (var i = 0; i < dataArr.length; i++){ new_data_arr.push( dataArr[i][0]/totleNb ); } totalYNomber = 10; // 運動相關 ctr = 1;//初始步驟 numctr = 50;//步驟 speed = 1.2; //毫秒 timer速度 //指示線 和 文字 lineStartAngle = -startAngle; line=40; //畫線的時候超出半徑的一段線長 textPadding=10; //文字與線之間的間距 textMoveDis = 200; //文字運動開始的間距 }
--繪製板塊圖例
drawMarkers(); //繪製比例圖及文字 function drawMarkers(){ ctx.textAlign="left"; for (var i = 0; i < dataArr.length; i++){ //繪製比例圖及文字 ctx.fillStyle = dataArr[i][1]; ctx.fillRect(posX, posY + 40 * i, tWidth, tHeight); ctx.moveTo(posX, posY + 40 * i); ctx.font = 'normal 24px 微軟雅黑'; //斜體 30像素 微軟雅黑字體 ctx.fillStyle = dataArr[i][1]; //"#000000"; var percent = dataArr[i][2] + ":" + parseInt(100 * new_data_arr[i]) + "%"; ctx.fillText(percent, textX, textY + 40 * i); } };
--繪製餅狀圖動畫(接着上一步的代碼寫在 goChart方法中 )
注:繪製餅狀圖動畫的方法連續的可能更利於查看,因此就沒有拆分開,做了必要的註釋,不理解的可留言
//繪製動畫 pieDraw(); function pieDraw(mouseMove){ for (var n = 0; n < dataArr.length; n++){ ctx.fillStyle = ctx.strokeStyle = dataArr[n][1]; ctx.lineWidth=1; var step = new_data_arr[n]* Math.PI * 2; //旋轉弧度 var lineAngle = lineStartAngle+step/2; //計算線的角度 lineStartAngle += step;//結束弧度 ctx.beginPath(); var x0=ox+radius*Math.cos(lineAngle),//圓弧上線與圓相交點的x座標 y0=oy+radius*Math.sin(lineAngle),//圓弧上線與圓相交點的y座標 x1=ox+(radius+line)*Math.cos(lineAngle),//圓弧上線與圓相交點的x座標 y1=oy+(radius+line)*Math.sin(lineAngle),//圓弧上線與圓相交點的y座標 x2=x1,//轉折點的x座標 y2=y1, linePadding=ctx.measureText(dataArr[n][2]).width+10; //獲取文本長度來肯定折線的長度 ctx.moveTo(x0,y0); //對x1/y1進行處理,來實現折線的運動 yMove = y0+(y1-y0)*ctr/numctr; ctx.lineTo(x1,yMove); if(x1<=x0){ x2 -= line; ctx.textAlign="right"; ctx.lineTo(x2-linePadding,yMove); ctx.fillText(dataArr[n][2],x2-textPadding-textMoveDis*(numctr-ctr)/numctr,y2-textPadding); }else{ x2 += line; ctx.textAlign="left"; ctx.lineTo(x2+linePadding,yMove); ctx.fillText(dataArr[n][2],x2+textPadding+textMoveDis*(numctr-ctr)/numctr,y2-textPadding); } ctx.stroke(); } //設置旋轉 ctx.save(); ctx.translate(ox, oy); ctx.rotate((Math.PI*2/numctr)*ctr/2); //繪製一個圓圈 ctx.strokeStyle = "rgba(0,0,0,"+ 0.5*ctr/numctr +")" ctx.beginPath(); ctx.arc(0, 0 ,(radius+20)*ctr/numctr, 0, Math.PI*2, false); ctx.stroke(); for (var j = 0; j < dataArr.length; j++){ //繪製餅圖 endAngle = endAngle + new_data_arr[j]* ctr/numctr * Math.PI * 2; //結束弧度 ctx.beginPath(); ctx.moveTo(0,0); //移動到到圓心 ctx.arc(0, 0, radius*ctr/numctr, startAngle, endAngle, false); //繪製圓弧 ctx.fillStyle = dataArr[j][1]; if(mouseMove && ctx.isPointInPath(mousePosition.x*2, mousePosition.y*2)){ ctx.globalAlpha = 0.8; } ctx.closePath(); ctx.fill(); ctx.globalAlpha = 1; startAngle = endAngle; //設置起始弧度 if( j == dataArr.length-1 ){ startAngle = endAngle = 90*Math.PI/180; //起始弧度 結束弧度 } } ctx.restore(); if(ctr<numctr){ ctr++; setTimeout(function(){ //ctx.clearRect(-canvas.width,-canvas.width,canvas.width*2, canvas.height*2); ctx.clearRect(-canvas.width, -canvas.height,canvas.width*2, canvas.height*2); drawMarkers(); pieDraw(); }, speed*=1.085); } }
--監聽鼠標移動,以實現移動到當前項做顏色變化(接着上一步的代碼寫在 goChart方法中 )
//監聽鼠標移動 var mouseTimer = null; canvas.addEventListener("mousemove",function(e){ e = e || window.event; if( e.offsetX || e.offsetX==0 ){ mousePosition.x = e.offsetX; mousePosition.y = e.offsetY; }else if( e.layerX || e.layerX==0 ){ mousePosition.x = e.layerX; mousePosition.y = e.layerY; } clearTimeout(mouseTimer); mouseTimer = setTimeout(function(){ ctx.clearRect(0,0,canvas.width, canvas.height); drawMarkers(); pieDraw(true); },10); });
--這樣咱們整個代碼就編寫完成了
所有代碼以下:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> canvas{ border: 1px solid #A4E2F9; } </style> </head> <body> <div height="400" width="600" style="margin:50px"> <canvas id="chart"> 你的瀏覽器不支持HTML5 canvas </canvas> </div> <script type="text/javascript"> function goChart(dataArr){ // 聲明所需變量 var canvas,ctx; // 圖表屬性 var cWidth, cHeight, cMargin, cSpace; // 餅狀圖屬性 var radius,ox,oy;//半徑 圓心 var tWidth, tHeight;//圖例寬高 var posX, posY, textX, textY; var startAngle, endAngle; var totleNb; // 運動相關變量 var ctr, numctr, speed; //鼠標移動 var mousePosition = {}; //線條和文字 var lineStartAngle,line,textPadding,textMoveDis; // 得到canvas上下文 canvas = document.getElementById("chart"); if(canvas && canvas.getContext){ ctx = canvas.getContext("2d"); } initChart(); // 圖表初始化 function initChart(){ // 圖表信息 cMargin = 20; cSpace = 40; canvas.width = canvas.parentNode.getAttribute("width")* 2 ; canvas.height = canvas.parentNode.getAttribute("height")* 2; canvas.style.height = canvas.height/2 + "px"; canvas.style.width = canvas.width/2 + "px"; cHeight = canvas.height - cMargin*2; cWidth = canvas.width - cMargin*2; //餅狀圖信息 radius = cHeight*2/6; //半徑 高度的2/6 ox = canvas.width/2 + cSpace; //圓心 oy = canvas.height/2; tWidth = 60; //圖例寬和高 tHeight = 20; posX = cMargin; posY = cMargin; // textX = posX + tWidth + 15 textY = posY + 18; startAngle = endAngle = 90*Math.PI/180; //起始弧度 結束弧度 rotateAngle = 0; //總體旋轉的弧度 //將傳入的數據轉化百分比 totleNb = 0; new_data_arr = []; for (var i = 0; i < dataArr.length; i++){ totleNb += dataArr[i][0]; } for (var i = 0; i < dataArr.length; i++){ new_data_arr.push( dataArr[i][0]/totleNb ); } totalYNomber = 10; // 運動相關 ctr = 1;//初始步驟 numctr = 50;//步驟 speed = 1.2; //毫秒 timer速度 //指示線 和 文字 lineStartAngle = -startAngle; line=40; //畫線的時候超出半徑的一段線長 textPadding=10; //文字與線之間的間距 textMoveDis = 200; //文字運動開始的間距 } drawMarkers(); //繪製比例圖及文字 function drawMarkers(){ ctx.textAlign="left"; for (var i = 0; i < dataArr.length; i++){ //繪製比例圖及文字 ctx.fillStyle = dataArr[i][1]; ctx.fillRect(posX, posY + 40 * i, tWidth, tHeight); ctx.moveTo(posX, posY + 40 * i); ctx.font = 'normal 24px 微軟雅黑'; //斜體 30像素 微軟雅黑字體 ctx.fillStyle = dataArr[i][1]; //"#000000"; var percent = dataArr[i][2] + ":" + parseInt(100 * new_data_arr[i]) + "%"; ctx.fillText(percent, textX, textY + 40 * i); } }; //繪製動畫 pieDraw(); function pieDraw(mouseMove){ for (var n = 0; n < dataArr.length; n++){ ctx.fillStyle = ctx.strokeStyle = dataArr[n][1]; ctx.lineWidth=1; var step = new_data_arr[n]* Math.PI * 2; //旋轉弧度 var lineAngle = lineStartAngle+step/2; //計算線的角度 lineStartAngle += step;//結束弧度 ctx.beginPath(); var x0=ox+radius*Math.cos(lineAngle),//圓弧上線與圓相交點的x座標 y0=oy+radius*Math.sin(lineAngle),//圓弧上線與圓相交點的y座標 x1=ox+(radius+line)*Math.cos(lineAngle),//圓弧上線與圓相交點的x座標 y1=oy+(radius+line)*Math.sin(lineAngle),//圓弧上線與圓相交點的y座標 x2=x1,//轉折點的x座標 y2=y1, linePadding=ctx.measureText(dataArr[n][2]).width+10; //獲取文本長度來肯定折線的長度 ctx.moveTo(x0,y0); //對x1/y1進行處理,來實現折線的運動 yMove = y0+(y1-y0)*ctr/numctr; ctx.lineTo(x1,yMove); if(x1<=x0){ x2 -= line; ctx.textAlign="right"; ctx.lineTo(x2-linePadding,yMove); ctx.fillText(dataArr[n][2],x2-textPadding-textMoveDis*(numctr-ctr)/numctr,y2-textPadding); }else{ x2 += line; ctx.textAlign="left"; ctx.lineTo(x2+linePadding,yMove); ctx.fillText(dataArr[n][2],x2+textPadding+textMoveDis*(numctr-ctr)/numctr,y2-textPadding); } ctx.stroke(); } //設置旋轉 ctx.save(); ctx.translate(ox, oy); ctx.rotate((Math.PI*2/numctr)*ctr/2); //繪製一個圓圈 ctx.strokeStyle = "rgba(0,0,0,"+ 0.5*ctr/numctr +")" ctx.beginPath(); ctx.arc(0, 0 ,(radius+20)*ctr/numctr, 0, Math.PI*2, false); ctx.stroke(); for (var j = 0; j < dataArr.length; j++){ //繪製餅圖 endAngle = endAngle + new_data_arr[j]* ctr/numctr * Math.PI * 2; //結束弧度 ctx.beginPath(); ctx.moveTo(0,0); //移動到到圓心 ctx.arc(0, 0, radius*ctr/numctr, startAngle, endAngle, false); //繪製圓弧 ctx.fillStyle = dataArr[j][1]; if(mouseMove && ctx.isPointInPath(mousePosition.x*2, mousePosition.y*2)){ ctx.globalAlpha = 0.8; } ctx.closePath(); ctx.fill(); ctx.globalAlpha = 1; startAngle = endAngle; //設置起始弧度 if( j == dataArr.length-1 ){ startAngle = endAngle = 90*Math.PI/180; //起始弧度 結束弧度 } } ctx.restore(); if(ctr<numctr){ ctr++; setTimeout(function(){ //ctx.clearRect(-canvas.width,-canvas.width,canvas.width*2, canvas.height*2); ctx.clearRect(-canvas.width, -canvas.height,canvas.width*2, canvas.height*2); drawMarkers(); pieDraw(); }, speed*=1.085); } } //監聽鼠標移動 var mouseTimer = null; canvas.addEventListener("mousemove",function(e){ e = e || window.event; if( e.offsetX || e.offsetX==0 ){ mousePosition.x = e.offsetX; mousePosition.y = e.offsetY; }else if( e.layerX || e.layerX==0 ){ mousePosition.x = e.layerX; mousePosition.y = e.layerY; } clearTimeout(mouseTimer); mouseTimer = setTimeout(function(){ ctx.clearRect(0,0,canvas.width, canvas.height); drawMarkers(); pieDraw(true); },10); }); } var chartData = [[50,"#2dc6c8","瓜子"], [100,"#b6a2dd", "花生"], [200,"#5ab1ee","土豆"], [700,"#d7797f","南瓜四號"]]; goChart(chartData); </script> </body> </html>
好了,今天就講到這裏,但願你們把代碼都本身敲一遍。
關注公衆號,博客更新便可收到推送