雷達(面積)圖
本章建議學習時間4小時
學習方式:詳細閱讀,並手動實現相關代碼(如果沒有canvas基礎,需要先學習前面的canvas基礎筆記)
學習目標:此教程將教會大家如何使用canvas繪制各種圖表,本次講解雷達(面積)圖。
源文件下載地址:https://github.com/sutianbinde/charts
雷達(面積)圖
雷達(面積)圖,我們的案例展示效果如下
功能:圖表可以根據數據自動變換比例,繪制中間范圍的時候有由小到大的動畫,鼠標移入到對應值會實現顏色變化,並且顯示當前項的詳細信息。
實現代碼相對來說比前面講的幾個圖表要簡單些些,具體代碼如下,相應的說明在代碼注釋中
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style type="text/css"> canvas{border: 1px solid #A4E2F9;} </style> </head> <body> <div id="chart" height="400" width="600" style="margin:30px;"></div> <script type="text/javascript"> function goChart(cBox,dataArr,textArr,ifFill){ // 聲明所需變量 var canvas,ctx; // 圖表屬性 var cWidth, cHeight, cMargin, cSpace; var originX, originY; // 主圖屬性 var tobalDots, maxValue; var lineStartAngle,radius; var colorArr; // 運動相關變量 var ctr, numctr, speed; //鼠標移動 var mousePosition = {}; // 創建canvas並獲得canvas上下文 canvas = document.createElement("canvas"); if(canvas && canvas.getContext){ ctx = canvas.getContext("2d"); } canvas.innerHTML = "你的瀏覽器不支持HTML5 canvas"; cBox.appendChild(canvas); initChart(); // 圖表初始化 // 圖表初始化 function initChart(){ // 圖表信息 cMargin = 60; cSpace = 60; //將canvas擴大2倍,然后縮小,以適應高清屏幕 canvas.width = cBox.getAttribute("width")* 2 ; canvas.height = cBox.getAttribute("height")* 2; canvas.style.height = canvas.height/2 + "px"; canvas.style.width = canvas.width/2 + "px"; //cHeight = canvas.height - cMargin*2-cSpace*2; //cWidth = canvas.width - cMargin*2-cSpace*2; originX = canvas.width/2; originY = canvas.height/2; // 柱狀圖信息 tobalDots = textArr.length; var allArr = []; for(var i=0; i<dataArr.length; i++){ allArr = allArr.concat( dataArr[i].value ); } maxValue = Math.max.apply(null,allArr); colorArr=["#AD93DB","#3AC9CB","#5FB2ED"]; //顏色數據 // 運動相關 ctr = 1; numctr = 40; speed = 1; textPadding=20; //文字與文字基線線之間的間距 lineStartAngle =Math.PI+ Math.PI/tobalDots; //起始繪制角度 radius = originY-cMargin-cSpace; //半徑 } drawLineLabelMarkers(); // 繪制圖表軸、標簽和標記 // 繪制圖表軸、標簽和標記 function drawLineLabelMarkers(){ ctx.font = "24px Arial"; ctx.lineWidth = 2; ctx.strokeStyle = "#DBDBDB"; ctx.fillStyle = "#000"; var startAngle = lineStartAngle; // 五個底圖環 for(var i=0; i<6; i++){ R = radius*(1-i/5); //五個 //畫一個環 ctx.beginPath(); for(var j=0; j<tobalDots+1; j++){ //多畫一個閉合路徑 startAngle = startAngle+2*Math.PI/tobalDots; var x = parseInt( originX+R*Math.cos(startAngle) ); var y = originY+R*Math.sin(startAngle); ctx.lineTo(x, y);//點連線 ctx.lineTo(originX, originY);//點到圓心連線 ctx.moveTo(x, y); //繪制文字 if(i==0 && textArr[j]){ drawMarkers(textArr[j],x,y) } } if(i%2==0){ ctx.fillStyle = "#ECECEC"; }else{ ctx.fillStyle = "#fff"; } ctx.closePath(); ctx.fill(); ctx.stroke(); } } // 繪制標記 function drawMarkers(text,x,y){ if(x<originX && y<=originY){ ctx.textAlign="right"; ctx.fillText(text, x-textPadding, y-textPadding); }else if(x<originX && y>originY){ ctx.textAlign="right"; ctx.fillText(text, x-textPadding, y+textPadding); }else if(y<=originY){ ctx.textAlign="left"; ctx.fillText(text, x+textPadding, y-textPadding); }else{ ctx.textAlign="left"; ctx.fillText(text, x+textPadding, y+textPadding); } }; drawChartAnimate(); // 繪制動畫 //繪制動畫 function drawChartAnimate(mouseMove){ var ifTip = false; var tipArr = null; //循環傳入的多組數據 for(var i=0; i<dataArr.length; i++){ var startAngle = lineStartAngle; var nowArr = dataArr[i].value; var arcArr = []; ctx.lineWidth = 4; ctx.fillStyle = ctx.strokeStyle = colorArr[i%colorArr.length]; ctx.beginPath(); for(var j=0; j<tobalDots; j++){ var R = radius*(nowArr[j]/maxValue)*ctr/numctr; startAngle = startAngle+2*Math.PI/tobalDots; var x = originX+R*Math.cos(startAngle); var y = originY+R*Math.sin(startAngle); //console.log(x,y); ctx.lineTo(x, y);//點連線 function drawArc(x,y,color,theTipArr){ return function(){ ctx.beginPath(); ctx.fillStyle = "#fff"; ctx.strokeStyle = color; ctx.lineWidth = 4*ctr/numctr; ctx.arc(x,y,6*ctr/numctr,0,Math.PI*2); if(!ifTip && mouseMove && ctx.isPointInPath(mousePosition.x*2, mousePosition.y*2)){ //如果是鼠標移動的到小點上,重新繪制圖表 //ctx.fillStyle = "rgba(46,199,201,1)"; //是否繪制提示 ifTip = true; tipArr = theTipArr; ctx.lineWidth *= 2; } ctx.fill(); ctx.stroke(); }; } arcArr.push( drawArc( x, y, colorArr[i%colorArr.length], [dataArr[i].name,nowArr[j],textArr[j]] ) ); //將繪制圓點方法利用閉包存起來,后面統一調用,數據多時顏色循環使用 } ctx.closePath(); if(ifFill){ ctx.globalAlpha = 0.3; ctx.fill(); ctx.globalAlpha = 1; } ctx.stroke(); for(var m=0; m<arcArr.length; m++){ arcArr[m](); } canvas.style.cursor = "default"; ifTip && drawTips(mousePosition.x*2, mousePosition.y*2,tipArr); } if(ctr<numctr){ ctr++; setTimeout(function(){ ctx.clearRect(0,0,canvas.width, canvas.height); drawLineLabelMarkers(); drawChartAnimate(); }, speed*=1.08); } } //繪制提示框 function drawTips(oX,oY,valArr){ canvas.style.cursor = "pointer"; ctx.save(); ctx.beginPath(); ctx.fillStyle = "rgba(0,0,0,0.5)"; var H = 120; roundedRect(ctx,oX+10,oY-H/2,2*H,H,5); ctx.fillStyle = "#fff"; ctx.textAlign="left"; ctx.fillText(valArr[0]+":", oX+20,oY-H/10); ctx.fillText(valArr[2]+":"+valArr[1], oX+20,oY+H/4); ctx.restore(); } //繪制圓角矩形的方法 function roundedRect(ctx,x,y,width,height,radius){ ctx.moveTo(x,x+radius); ctx.beginPath(); ctx.lineTo(x,y+height-radius); ctx.quadraticCurveTo(x,y+height,x+radius,y+height); ctx.lineTo(x+width-radius, y+height); ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius); ctx.lineTo(x+width,y+radius); ctx.quadraticCurveTo(x+width,y,x+width-radius,y); ctx.lineTo(x+radius,y); ctx.quadraticCurveTo(x,y,x,y+radius); ctx.closePath(); ctx.fill(); } //監聽鼠標移動 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); drawLineLabelMarkers(); drawChartAnimate(true); },10); }); } var dataArr = [ { value : [20000, 10000, 28000, 35000, 50000, 19000], name : '預算分配' }, { value : [15000, 14000, 28000, 31000, 42000, 21000], name : '實際開銷' } ]; /* * 參數1 :需要顯示canvas的dom (非canvas標簽,需要指定height和width) * 參數2:二維數據 每個數據表示需要顯示的一組數據對象 (value表示數據數組,name表示此數據名稱) * 參數3:一維數組 對應上面每個數據的名字 * 參數4:中部填充是否實心 ,默認false * */ goChart(document.getElementById("chart"),dataArr,["銷售","管理","信息技術","客服","研發","市場"],true) </script> </body> </html>
希望大家把代碼都自己敲一遍。
關注公眾號,博客更新即可收到推送