html5 canvas 繪製曲線圖
阿新 • • 發佈:2019-02-19
程式碼: <!Doctype Html> <html> <head> <title>Line Chart Demo</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <style> body{background-color:#bfd0c4;} canvas{background-color:white;position:absolute;top:50%;left:50%;margin-left:-350px;margin-top:-250px;box-shadow: 3px 3px 3px #7e7a7c;} </style> </head> <body> <canvas width="700" height="400" id="canvas"> </canvas> <script> window.onload = function(){ var multiData = {values:[ { value0:[ {x:"Jan",y:5}, {x:"Feb",y:5}, {x:"Mar",y:80}, {x:"Apr",y:10}, {x:"May",y:30}, {x:"Jun",y:30}, {x:"Jul",y:60}, {x:"Aug",y:10} ]}, { value1:[ {x:"Jan",y:50}, {x:"Feb",y:40}, {x:"Mar",y:60}, {x:"Apr",y:10}, {x:"May",y:20}, {x:"Jun",y:10}, {x:"Jul",y:40}, {x:"Aug",y:10} ]} ] }//必須按照這個格式定義資料,關鍵字values value0 value1 ...... /* *@param0: canvas 的id *@param1: json 資料 *@param2: 座標距離畫布的間隙padding *@param3: 如果只有一條資料時資料的顏色,多條資料顏色隨機 *@param4: 點的顏色 *@param5: 是否繪製背景線 *@param6: 是否是多條資料 */ //先定義資料線的名字,再繪製資料 LineChart.setKey(["2013","2014"]); LineChart.setData("canvas",multiData,60,"red","#333",true,true); } var LineChart={ keynames:[],//資料資訊陣列 can:undefined, ctx:undefined, width:undefined, lineColor:undefined, dotColor:undefined, isBg:false, isMultiData:false, setData:function(canId,data,padding,lineColor,dotColor,isBg,isMultiData){ this.lineColor = lineColor; this.dotColor = dotColor; this.can = document.getElementById(canId); this.ctx = this.can.getContext("2d"); this.isBg = isBg; this.isMultiData = isMultiData; this.drawXY(data,0,padding,this.can); }, isMultiData:function(data){ if(data.values.length>1){ this.isMultiData = true; } },//是否是多條資料線 drawXY:function(data,key,padding,can){ this.ctx.lineWidth="4"; this.ctx.strokeStyle="black"; this.ctx.font = 'italic 15px sans-serif'; this.ctx.beginPath(); this.ctx.moveTo(padding,0) this.ctx.lineTo(padding,can.height-padding); this.ctx.lineTo(can.width,can.height-padding); this.ctx.stroke(); var perwidth = this.getPixel(data,key,can.width,padding);//x 軸每一個數據佔據的寬度 var maxY = this.getMax(data,0,this.isMultiData);//獲得Y軸上的最大值 var yPixel = this.getYPixel(maxY,can.height,padding).pixel; var ycount = this.getYPixel(maxY,can.height,padding).ycount; for( var i=0,ptindex;i< data.values[key]["value"+key].length;i++ ){ ptindex = i+1; var x_x = this.getCoordX(padding,perwidth,ptindex); var x_y = can.height-padding+20; this.ctx.fillText(data.values[key]["value"+key][i].x,x_x,x_y,perwidth); } this.ctx.textAlign = "right"//y軸文字靠右寫 this.ctx.textBaseline = "middle";//文字的中心線的調整 for(var i=0;i< ycount/10;i++){ this.ctx.fillText(i*10,padding-10,(ycount/10-i)*10*yPixel,perwidth); } if(this.isBg){ var x = padding; this.ctx.lineWidth="1"; this.ctx.strokeStyle="#e8e8e8"; for( var i=0;i< ycount/10;i++ ){ var y = (ycount/10-i)*10*yPixel; this.ctx.moveTo(x,y); this.ctx.lineTo(can.width,y); this.ctx.stroke(); } }//選擇繪製背景線 this.ctx.closePath(); this.drawData(data,0,padding,perwidth,yPixel,this.isMultiData); },//繪製XY座標 線 以及點 drawData:function(data,key,padding,perwidth,yPixel,isMultiData,lineColor){ if(!isMultiData){ var keystr = "value"+key; this.ctx.beginPath(); this.ctx.lineWidth="2"; if(arguments[6]){ this.ctx.strokeStyle=lineColor; }else{ this.ctx.strokeStyle=this.lineColor; } var startX = this.getCoordX(padding,perwidth,0); var startY = this.getCoordY(padding,yPixel,data.values[key][keystr][0].y); this.ctx.beginPath(); this.ctx.lineWidth="2"; for( var i=0;i< data.values[key][keystr].length;i++ ){ var x = this.getCoordX(padding,perwidth,i+1); var y = this.getCoordY(padding,yPixel,data.values[key][keystr][i].y); this.ctx.lineTo(x,y); } this.ctx.stroke(); this.ctx.closePath(); /*下面繪製資料線上的點*/ this.ctx.beginPath(); this.ctx.fillStyle=this.dotColor; for( var i=0;i< data.values[key][keystr].length;i++ ){ var x = this.getCoordX(padding,perwidth,i+1); var y = this.getCoordY(padding,yPixel,data.values[key][keystr][i].y); this.ctx.moveTo(x,y); this.ctx.arc(x,y,3,0,Math.PI*2,true);//繪製資料線上的點 this.ctx.fill(); } this.ctx.closePath(); }else{//如果是多條資料線 for( var i=0;i< data.values.length;i++ ){ var color = "rgb("+parseInt(Math.random()*256)+","+parseInt(Math.random()*256)+", "+parseInt(Math.random()*256)+")"; LineChart.drawData(data,i,padding,perwidth,yPixel,false,color); LineChart.drawKey(color,this.keynames[i],padding,i); } } },//繪製資料線和資料點 getPixel:function(data,key,width,padding){ var count = data.values[key]["value"+key].length; return (width-20-padding)/(count+(count-1)*1.5); },//寬度 getCoordX:function(padding,perwidth,ptindex){//下標從1開始 不是從0開始 return 2.5*perwidth*ptindex+padding+10-2*perwidth; },//橫座標X 隨ptindex 獲得 getCoordY:function(padding,yPixel,value){ var y = yPixel*value; return this.can.height-padding-y; },//縱座標X 隨ptindex 獲得(注意 縱座標的演算法是倒著的因為原點在最上面) getYPixel:function(maxY,height,padding){ var ycount = (parseInt(maxY/10)+1)*10+10;//y軸最大值 return {pixel:(height-padding)/ycount,ycount:ycount}; },//y軸的單位長度 getMax:function(data,key,isMultiData){ if(!isMultiData){ var maxY = data.values[key]["value"+key][0].y; var length = data.values[key]["value"+key].length; var keystr = "value"+key; for( var i=1;i< length;i++ ){ if(maxY< data.values[key][keystr][i].y) maxY=data.values[key][keystr][i].y; } return maxY;//返回最大值 如果不是多資料 }else{ var maxarr=[]; var count = data.values.length;//多條資料的資料長度 for(var i=0;i< count;i++){ maxarr.push(LineChart.getMax(data,i,false)); } var maxvalue = maxarr[0]; for(var i=1;i< maxarr.length;i++){ maxvalue = (maxvalue< maxarr[i])?maxarr[i]:maxvalue; } return maxvalue; }//如果是多資料 }, setKey:function(keynames){//keynames 是陣列 for(var i=0;i< keynames.length;i++){ this.keynames.push(keynames[i]);//存入陣列中 } }, drawKey:function(color,keyname,padding,lineindex){ var x = padding+10; var y = this.can.height - padding+20+13*(lineindex+1); this.ctx.beginPath(); this.ctx.strokeStyle = color; this.ctx.font="10px"; this.ctx.moveTo(x,y); this.ctx.lineTo(x+50,y); this.ctx.fillText(":"+keyname,x+80,y,30); this.ctx.stroke(); this.ctx.closePath(); } } </script> </body> </html>
繪製曲線圖的要點:
1.有一個畫布,在畫布上進行圖表的顯示
2.資料(陣列格式、json等格式)
3.如何繪製x/y軸(找到x/y軸資料的最大值、最小值,確定原點、最大值及平均單位,進行繪製。繪製標題)
4.如何繪製資料線、資料點(同上)