[HTML5系列实践之一]用HTML5做动态饼图

注:原创文章,转载注明原作者为dunhuangmi。

 

通常根据数据生成统计图,有柱状图,饼状图,折线图等等不同类型。柱状图可以通过js控制css的变化实现,比较简单。但是画圆必须用flash、svg或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);
	}

 drawfun()代码

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值