1. 程式人生 > >html5 canvas 繪製曲線圖

html5 canvas 繪製曲線圖

程式碼:
<!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.如何繪製資料線、資料點(同上)