[HTML5系列實踐之一]用HTML5做動態餅圖

注:原創文章,轉載註明原作者爲dunhuangmi。

通常根據數據生成統計圖,有柱狀圖,餅狀圖,折線圖等等不同類型。柱狀圖可以通過js控制css的變化實現,比較簡單。但是畫圓必須用flash或html5來實現。

下面介紹一下用html5實現如下餅圖的原理




我們都知道,用html5繪圖需要使用CanvasRenderingContext2D對象提供的各種API,畫圓需要用到arc()。
它的寫法是arc(x, y, radius, startAngle, endAngle, counterclockwise),具體含義可查手冊。
於是我們畫一個π/6(30度)扇形(從0度開始,逆時針)就會是這樣:

funciton draw(){
	canvas = document.getElementById('tutorial');
	if (canvas.getContext){
		var ctx = canvas.getContext('2d');
		ctx.fillStyle ="#3666B0";  
		ctx.strokeStyle = "#3666B0"; 
		ctx.moveTo(300,200); 
		ctx.arc(300,200,150,0,-Math.PI*/6,true);
		ctx.fill();  
		ctx.stroke(); 
	}
}


body部分這樣寫:
<body οnlοad="draw();">
    <canvas id="tutorial" width="700" height="400"><div class="nohtml5">你的瀏覽器不支持html5</div></canvas>
</body>





需求是畫餅圖,因此要畫一組扇形組成一個完整的圓。於是設置一組全局變量數組控制扇形的角度和顏色。

        var color = ["#27255F","#2F368F","#3666B0","#2CA8E0","#77D1F6"];  /*5組扇形,不同顏色*/
        var data = [5,30,15,30,20];   /*扇形的角度百分比,5組加起來正好是100*/
        var startPoint = 0;  /*其實點位置*/
        var ctx;
        var o={x:300,y:200}/*座標原點*/,rectbox={ox:0,oy:0,wid:700,hei:400}/*畫布大小*/,radius=150/*圓半徑*/;

在draw()函數中用循環來控制連續畫扇區

	for (var i=0;i<data.length ; i++)
	{
		drawfan(i);
	}

drawfan()代碼

function drawfan(seq){
	ctx.fillStyle =color[seq];  
	ctx.strokeStyle = color[seq]; 
	ctx.moveTo(o.x,o.y); 
	ctx.arc(o.x,o.y,radius,startPoint,startPoint-Math.PI*2*(data[seq]/100),true);
	ctx.fill();  
	ctx.stroke(); 
	startPoint -= Math.PI*2*(data[seq]/100);
}

餅圖效果還不錯噠:

下面要實現當鼠標位於某扇區之上,該扇區浮動放大的效果,如下圖效果:


1、扇區放大,放大radius即可實現

2、要實現浮動效果,一方面在扇區外加畫陰影,另一方面,應將放大的扇形原點偏移出原先的原點。新原點的x,y座標應根據扇區的中心點角度與半徑偏移量計算而得。因此必須還要計算每個扇形的始末角度值。還要用sin,cos函數分別計算座標。這裏有一系列的算法。不記得中學數學的要去查查書了啊。注意網頁的x軸y軸與數學上的座標系方向不同,網頁座標原點在左上角,y軸的方向與數學座標系的相反。

3、要判斷鼠標此時是否在某扇區內,如果在,則放大浮動該扇區。

4、還要實現扇區漸進放大的效果,用setInterval()來實現。

5、鼠標位於扇區之上時增加一個浮動說明框(div+css實現,很簡單,不贅述,只是跟隨鼠標做位置變化即可)

增加全局變量:

var angle=new Array(5);  /*扇形的始末角度*/
var hlnumber=3/*選中哪一塊扇形*/,prenumber=-1/*已放大扇區序號*/,hlt,animating=false/*是否在動畫中*/;
var distance=8,/*選中扇區浮出距離*/expand=1.08,/*放大倍數*/shadow={blur:10,x:10,y:10};/*陰影的參數*/
var diffinfo={x:20,y:10}/*信息浮動框與鼠標的距離*/

計算扇區角度區間

function init(){	//計算每個扇區的角度區間
	var sum=0;
	for (var i=0;i<data.length ;i++ )
	{
		angle[i]=new Array(2);
		angle[i][0]=sum;
		sum+=data[i]/100;
		angle[i][1]=sum;
	}
}

判斷鼠標此時位於哪個扇區之上

function whichfan(x,y){	//計算某座標點屬於哪個扇區,圓外返回-1
	var nx=x-o.x, ny=o.y-y;
	if (((nx*nx)+(ny*ny))>radius*radius){
		return -1;
	}
	var ap=Math.atan2(ny,nx);
	if (ap<0)
	{
		ap+=Math.PI*2;
	}
	//alert(ap/Math.PI/2);
	for (var i=0;i<angle.length ;i++ )
	{
		if (((ap/Math.PI/2)>=angle[i][0])&&((ap/Math.PI/2)<=angle[i][1]))
		{
			return i;
		}
	}
	return angle.length;		
}

改寫drawfan(),增加了兩個參數:dist(放大扇區的偏移量),ifshadow(是否有陰影)

function drawfan(seq,dist,ifshadow){
	ctx.fillStyle =color[seq];  
	ctx.strokeStyle = color[seq]; 
	ctx.beginPath();  
			
	if (ifshadow){
		var ang=(angle[seq][0]+angle[seq][1])/2;
		var newox,newoy;
		newox=o.x+dist* Math.cos(ang*2*Math.PI);
		newoy=o.y-dist* Math.sin(ang*2*Math.PI);
		ctx.shadowColor =  '#cccccc';
		ctx.shadowBlur = shadow.blur;  //設置陰影模糊程度。此值越大,陰影越模糊
		ctx.shadowOffsetX = shadow.x * Math.cos(ang*2*Math.PI);   //陰影的x和y偏移量,單位是像素。
		ctx.shadowOffsetY = -shadow.y * Math.sin(ang*2*Math.PI);	
		ctx.moveTo(newox,newoy); 		
		ctx.arc(newox,newoy,radius*expand,startPoint,startPoint-Math.PI*2*(data[seq]/100),true); 
	}
	else{
		ctx.shadowBlur = 0;  //設置陰影模糊程度。此值越大,陰影越模糊
		ctx.shadowOffsetX = 0;   //陰影的x和y偏移量,單位是像素。
		ctx.shadowOffsetY = 0;	
		ctx.moveTo(o.x,o.y); 
		ctx.arc(o.x,o.y,radius,startPoint,startPoint-Math.PI*2*(data[seq]/100),true);
	}
	ctx.fill();  
	ctx.stroke(); 
	startPoint -= Math.PI*2*(data[seq]/100);
}

onMousemove觸發的事件:

function draw(){
	....
	canvas.addEventListener('mousemove',showinfo,false);
}
?
function showinfo(){
	....
	if(window.event){
		mouse.innerHTML='x:'+(window.event.x)+';y:'+(window.event.y);
		p.x=window.event.
x; p.y=window.event.y; } else{ mouse.innerHTML='x:'+(event.pageX)+';y:'+(event.pageY); p.x=event.pageX; p.y=event.pageY; } var fnumber=whichfan(p.x,p.y); if ((fnumber!=-1) &&(fnumber!=data.length)) { if (prenumber==fnumber) { infofan.style.left=p.x+diffinfo.x+'px'; infofan.style.top=p.y+diffinfo.y+'px'; return; } var internal=0; animating=true; hlt=setInterval(function(){ ctx.clearRect(rectbox.ox,rectbox.oy,rectbox.wid,rectbox.hei); if (internal>distance){ clearInterval(hlt); animating=false; } for(var j=0;j<data.length;j++){ if (j!=fnumber){ drawfan(j,0); } else{ drawfan(fnumber,internal,true); internal++; } } },20); prenumber=fnumber; infofan.style.left=p.x+diffinfo.x+'px'; infofan.style.top=p.y+diffinfo.y+'px'; infofan.innerHTML='這是一個扇區。它代表'+data[fnumber]+'%的統計類型。'; infofan.style.display='block'; //hlt=setInterval("drawhlfan(5,i);",500); return; } prenumber=-1; startPoint =0;// 1.5 * Math.PI; ctx.clearRect(rectbox.ox,rectbox.oy,rectbox.wid,rectbox.hei); for(var j=0;j<data.length;j++){ drawfan(j,0); } infofan.style.display='none'; } 
?

代碼和最終效果參見:http://dunhuang.a67.cnaaa1.com/html5/canvas-arc6.html,原創文章,轉載註明原作者爲dunhuangmi。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章