www.gusucode.com > 一款使用方便prototype的前端直方图表插件源码程序 > 一款使用方便prototype的前端直方图表插件/plotr0[1].3.0/plotr-0[1].3.0/lib/plotr/PieChart.js

    /*
	Plotr.PieChart
	==============	
	Plotr.PieChart is part of the Plotr Charting Framework.
	Download by http://www.srcfans.com
	For license/info/documentation: http://www.solutoire.com/plotr/
	
	Credits
	-------
	Plotr is partially based on PlotKit (BSD license) by
	Alastair Tse <http://www.liquidx.net/plotkit>.
	
	Copyright
	---------
 	Copyright 2007 (c) Bas Wenneker <sabmann[a]gmail[d]com>
 	For use under the BSD license. <http://www.solutoire.com/plotr>
*/

if (typeof(Plotr.Base) == 'undefined' || 
	typeof(Plotr.Chart) == 'undefined'){
			
	throw 'Plotr.PieChart depends on Plotr.{Base, Chart}.';
}

Plotr.PieChart = Class.create();
Object.extend(Plotr.PieChart.prototype, Plotr.Chart);
Object.extend(Plotr.PieChart.prototype,{
	type: 'pie',
	
/*
	Function: render
		Renders the chart with the specified options. The optional parameters can be used to render a piechart in a different canvas element with new options.
		
	Parameters:
		element - (Optional) canvas element to render a chart in.
		options - (Optional) options for this chart.
*/
	render: function(/*String*/element, /*Object*/options) {	
		
		if(this.isIE && this._ieWaitForVML(element,options)){
			return;
		}

		this._evaluate(options);
		this._render(element);
		this._renderPieChart();
		this._renderPieAxis();
		
		if(this.isIE){
			for(var el in this.renderStack){
				if(typeof(this.renderStack[el]) != 'function'){
					this.render(el,this.renderStack[el]);
					break;
				}
			}
		}
	},	
	
/*
	Function: _evaluate
		(Private) Evaluates all the data needed to plot the pie chart.
		
	Parameters:
		options - options for this chart.
*/
	_evaluate: function(/*Object*/options) {
		this._eval(options);
		this._evalPieChart();
		this._evalPieTicks();			
	},
	
/*
	Function: _evalPieChart
		(Private) Evaluates measures for pie charts.
*/
	_evalPieChart: function(){
		
		var slices = this.dataSets.collect(function(hash, index){
			
			return {
				name:	hash[0], // TODO Real labels
				value:	[index, hash[1][0][1]]
			};
		});
		var sum = slices.pluck('value').pluck(1).inject(0, function(acc, n) { return acc + n; });
		
		var fraction = angle = 0.0;
		this.slices = slices.collect(function(slice){
			angle += fraction;
			if(slice.value[1] > 0){
				fraction = slice.value[1]/sum;
				return {
					name: 		slice.name,
					fraction: 	fraction,
					xval: 		slice.value[0],
					yval: 		slice.value[1],
					startAngle: 2 * angle * Math.PI,
					endAngle: 	2 * (angle + fraction) * Math.PI
				};				
			}
		});
	},
	
/*
	Function: _renderPieChart
		(Private) Renders a pie chart.
*/	
	_renderPieChart: function(){
		var cx = this.canvasNode.getContext('2d');
				
		var centerx = this.area.x + this.area.w * 0.5;
    	var centery = this.area.y + this.area.h * 0.5;
		var radius = Math.min(this.area.w * this.options.pieRadius, this.area.h * this.options.pieRadius);
		
		if(this.isIE){
	        centerx = parseInt(centerx,10);
	        centery = parseInt(centery,10);
	        radius = parseInt(radius,10);
	    }
		
		var drawPie = function(slice){
			cx.beginPath();
			cx.moveTo(centerx, centery);
			cx.arc(centerx, centery, radius, 
                    slice.startAngle - Math.PI/2,
                    slice.endAngle - Math.PI/2,
                    false);
			cx.lineTo(centerx, centery);
       		cx.closePath();
		};
				
		if(this.options.stroke.shadow){
			cx.save();
			cx.fillStyle = "rgba(0,0,0,0.15)";
				
	        cx.beginPath();
			cx.moveTo(centerx, centery);
			cx.arc(centerx+1, centery+2, radius+1, 0, Math.PI*2, false);
			cx.lineTo(centerx, centery);
       		cx.closePath();
			cx.fill();
			cx.restore();
		}
		
		cx.save();
		this.slices.each(function(slice,i){
						
			if(Math.abs(slice.startAngle - slice.endAngle) > 0.001){
				
				cx.fillStyle = new Plotr.Color(this.options.colorScheme[slice.name]).toRgbaString(parseFloat(this.options.fillOpacity));;
				
				if(this.options.shouldFill){
					drawPie(slice);               	
	                cx.fill();
	            }
	            
	            if(!this.options.stroke.hide){
					drawPie(slice);
	                cx.lineWidth = this.options.stroke.width;
	                cx.strokeStyle = this.options.stroke.color;
											       
	                cx.stroke();
	            }
			}
			
		}.bind(this));
		cx.restore();
		
	},
	
/*
	Function: _evalPieTicks
		(Private) Evaluates ticks for x and y axis.
*/
	_evalPieTicks: function(){
				
		this.xticks = [];
				
		if(this.options.axis.x.ticks){
			
			var lookup = [];
			this.slices.each(function(slice){
				lookup[slice.xval] = slice;
			});
			
			this.options.axis.x.ticks.each(function(tick){
				var slice = lookup[tick.v]; 
	            var label = tick.label || tick.v.toString();
				if(!!(slice)){
					label += ' (' + (slice.fraction * 100).toFixed(1) + '%)';
					this.xticks.push([tick.v, label]);
				}
			}.bind(this));
			
		}else{
			
			this.slices.each(function(slice){
				var label = slice.xval + ' (' + (slice.fraction * 100).toFixed(1) + '%)';
				this.xticks.push([slice.xval, label]);
			}.bind(this));			
		}		
	},

/*
	Function: _renderPieAxis
		(Private) Renders the axis for pie charts.
*/
	_renderPieAxis: function(){
		
		if(this.options.axis.x.hide || !this.xticks){
        	return;
		}			
		this.xlabels = lookup = [];
		this.slices.each(function(slice){
			lookup[slice.xval] = slice;
		});
		
		var centerx = this.area.x + this.area.w * 0.5;
	    var centery = this.area.y + this.area.h * 0.5;
	    var radius = Math.min(this.area.w * this.options.pieRadius, this.area.h * this.options.pieRadius);
		var labelWidth = this.options.axis.labelWidth;
		
		var labelStyle = {
	        position: 	'absolute',
	        zIndex: 	11,
	        width: 		labelWidth + 'px',
	        fontFamily: this.options.axis.labelFont,
	        fontSize: 	this.options.axis.labelFontSize + 'px',
	        overflow: 	'hidden',
	        color: 		this.options.axis.labelColor
	    }; 
		
		this.xticks.each(function(tick){
			var slice = lookup[tick[0]];
			
			// normalize the angle
			var normalisedAngle = (slice.startAngle + slice.endAngle)/2;
			if(normalisedAngle > Math.PI * 2){
				normalisedAngle = normalisedAngle - Math.PI * 2;
			}else if(normalisedAngle < 0){
				normalisedAngle = normalisedAngle + Math.PI * 2;
			}
				
			var labelx = centerx + Math.sin(normalisedAngle) * (radius + 10);
	        var labely = centery - Math.cos(normalisedAngle) * (radius + 10);
						
			if(normalisedAngle <= Math.PI * 0.5){
	            // text on top and align left
				Object.extend(labelStyle, {
					textAlign: 		'left',
					verticalAlign: 	'top',
					left: 			labelx + 'px',
					top: 			(labely - this.options.axis.labelFontSize) + 'px'
				});
	        }else if((normalisedAngle > Math.PI * 0.5) && (normalisedAngle <= Math.PI)){
	            // text on bottom and align left
				Object.extend(labelStyle, {
					textAlign: 		'left',
					verticalAlign: 	'bottom',
					left: 			labelx + 'px',
					top: 			labely + 'px'
				});	
	        }else if((normalisedAngle > Math.PI) && (normalisedAngle <= Math.PI*1.5)){
	            // text on bottom and align right
				Object.extend(labelStyle, {
					textAlign: 		'right',
					verticalAlign: 	'bottom',
					left: 			(labelx  - labelWidth) + 'px',
					top: 			labely + 'px'
				});
	        }else {
	            // text on top and align right
				Object.extend(labelStyle, {
					textAlign: 		'right',
					verticalAlign: 	'bottom',
					left: 			(labelx  - labelWidth) + 'px',
					top: 			(labely - this.options.axis.labelFontSize) + 'px'
				});
	        }
			
			var label = Element.setStyle(document.createElement('div'), labelStyle);
			label.appendChild(document.createTextNode(tick[1]));
			
            this.htmlWrapper.appendChild(label);
			this.xlabels.push(label);
			
		}.bind(this));		
	}
});