纯HTML5 Canvas实现的饼图详解编程语言

基本思路:

主要是利用HTML5 Canvas实现饼图绘制,绘制弧度的API主要是使用

context.arc与lineto两个API。

实现的功能有:

1. 支持标签Legend显示或者隐藏

2. 首次载入动画效果

3. 鼠标tooltip效果

4. 自定义饼图大小与是否添加文字

效果如下:

纯HTML5 Canvas实现的饼图详解编程语言

调用代码:

<html> 
<head> 
<meta http-equiv="X-UA-Compatible" content="chrome=IE8"> 
<script src="js/fishcomponent.js"></script> 
<script src="js/pie.js"></script> 
<title>My Demo 1</title> 
	<script> 
	window.onload = function() { 
		var canvas = document.getElementById("pie_canvas"); 
		var seriesData = [{name:"apples", value:150, color:"RGBA(255,0,0,1)"}, 
		              {name:"orange", value:100, color:"RGBA(255,255,0,1)"}, 
		              {name:"banana", value:80, color:"RGBA(255,0,255,1)"}, 
		              {name:"peaches", value:60, color:"RGBA(0,255,255,1)"}, 
		              {name:"strawberries", value:40, color:"RGBA(0,127,255,1)"}] 
		var config = { 
				width : 600,  
				height: 400, 
				series: seriesData, 
				canvas: canvas, 
				unit: "kg", 
				title:"Fruit Sales", 
 				tooltips : { 
					enable : true 
				}, 
				animation :{ 
					enable: true 
				}, 
			    legend : { 
			    	enable : true 
			    }, 
			    text : { 
			    	enable: true 
			    }, 
		}; 
		pieChart.initSettings(config); 
		pieChart.render(); 
	} 
	</script> 
</head> 
<body> 
<h1>Pie Chart Demo</h1> 
<div id="my_container" style="width:600px; height:500px;"> 
	<canvas id="pie_canvas"></canvas> 
</div> 
<div id="btn-group"> 
	<button type="button" id="clear-button">Clear Plot</button> 
	<button type="button" id="refresh-button">Draw Plot</button> 
</div> 
</body> 
</html>

饼图JS的代码:

var pieChart = { 
width: 600, 
height: 400, 
series: [], 
unit: "kg", 
chartCanvas: null, 
selectable : true, 
title: "Pie Chart", 
legend : { 
enable : true 
}, 
edge : { 
width: 50, 
height: 50 
}, 
animation: { 
enable: true, 
animCanvas : null, 
hh: 1, // trick is here!! for animation play 
pctx: null 
}, 
tooltips: { 
enable: true, 
tooltipCanvas : null, 
ttContext: null, 
index: -1 
}, 
circle : { 
cx: 0, 
cy: 0, 
radius: 0 
}, 
text : { 
enable: false, 
content:[] 
}, 
initSettings: function (config) { 
this.chartCanvas = config.canvas; 
this.chartCanvas.width = config.width; 
this.chartCanvas.height = config.height; 
this.width = config.width; 
this.height = config.height; 
this.series = config.series; 
this.title = config.title; 
this.unit = config.unit; 
if(config.tooltips != undefined) { 
this.tooltips.enable = config.tooltips.enable;        	 
} 
if(config.animation != undefined) { 
this.animation.enable = config.animation.enable;        	 
} 
if(config.legend != undefined) { 
this.legend.enable = config.legend.enable;        	 
} 
if(config.text != undefined) { 
this.text.enable = config.text.enable; 
} 
}, 
render : function() { 
// initialization circle 
this.circle.cx = this.width/2; 
this.circle.cy = this.height/2; 
this.circle.radius = Math.min(this.width/2, this.height/2) - Math.max(this.edge.width, this.edge.height); 
var ctx = null; 
if(this.animation.enable) { 
this.animation.animCanvas = document.createElement("canvas"); 
this.animation.animCanvas.width = this.width; 
this.animation.animCanvas.height = this.height; 
ctx = this.animation.animCanvas.getContext("2d"); 
} else { 
ctx = this.chartCanvas.getContext("2d"); 
this.renderBorder(ctx); 
} 
if(this.circle.radius <= 0) { 
ctx.strokeText("Can not reader the chart, Circle is too small."); 
return; 
} 
// draw each arc according to data series  
var sum = 0; 
var nums = this.series.length; 
for(var i=0; i<nums; i++) { 
sum += this.series[i].value; 
} 
// draw title 
ctx.font = '18pt Calibri'; 
ctx.fillText(this.title, this.width/2 - this.edge.width, 30); 
ctx.save(); 
var deltaArc = 0; 
for(var i=0; i<nums; i++) { 
var precent = this.series[i].value/sum; 
this.renderPie(ctx, i, precent, deltaArc); 
deltaArc += 2*Math.PI * precent; 
} 
ctx.restore(); 
// add blur shadow 
ctx.save(); 
ctx.shadowColor = "black"; 
ctx.shadowOffsetX = 0; 
ctx.shadowOffsetY = 0; 
ctx.shadowBlur = 10; 
ctx.beginPath(); 
ctx.arc(this.circle.cx, this.circle.cy, this.circle.radius, 0, Math.PI * 2, false); 
ctx.closePath(); 
ctx.lineWidth = 1; 
ctx.strokeStyle = "RGBA(127,127,127,1)"; 
ctx.stroke(); 
ctx.restore(); 
// render legend 
ctx.save(); 
this.renderLegend(ctx, sum); 
ctx.restore(); 
// play animation 
if(this.animation.enable) { 
var parent = this; 
this.animation.pctx = this.chartCanvas.getContext("2d"); 
this.renderBorder(this.animation.pctx); 
setTimeout(function() {parent.playAnimation(parent);}, 1000/20);   	    		 
} 
}, 
showTooltips : function(loc, ctx) { 
if(!this.tooltips.enable) { 
return; 
} 
var dx = loc.x - this.width/2; 
var dy = loc.y - this.height/2; 
var dis = Math.floor(Math.sqrt(dx * dx + dy * dy)); 
if(dis <= this.circle.radius) { 
// draw tool tip text 
var angle = Math.atan2(dy,dx); 
if(angle <= 0) { 
// if[-Math.PI, 0], make it[Math.PI, 2*Math.PI] 
angle = angle + 2*Math.PI; 
} 
var sum = 0; 
var nums = this.series.length; 
for(var s=0; s<nums; s++) { 
sum += this.series[s].value; 
} 
var deltaArc = 0; 
var index = 0; 
for(var i=0; i<nums; i++) { 
var precent = this.series[i].value/sum; 
deltaArc += 2*Math.PI * precent; 
if(angle<=deltaArc) { 
index = i; 
break; 
} 
} 
if(this.tooltips.tooltipCanvas == null) { 
this.tooltips.tooltipCanvas = document.createElement("canvas"); 
this.tooltips.ttContext = this.tooltips.tooltipCanvas.getContext("2d"); 
this.tooltips.tooltipCanvas.width = 150; 
this.tooltips.tooltipCanvas.height = 100; 
}  
// only draw once 
// if(index == this.tooltips.index){ 
// 	return; 
// } 
this.clearTooltips(ctx); 
this.tooltips.index = index; 
var m_context = this.tooltips.ttContext; 
m_context.save(); 
m_context.clearRect(0, 0, this.tooltips.tooltipCanvas.width, this.tooltips.tooltipCanvas.height); 
m_context.lineWidth = 2; 
m_context.strokeStyle = this.series[index].color; 
m_context.fillStyle="RGBA(255,255,255,0.7)"; 
// m_context.strokeRect(2, 2, this.tooltips.tooltipCanvas.width-4, this.tooltips.tooltipCanvas.height-4); 
// m_context.fillRect(2,2,this.tooltips.tooltipCanvas.width-4, this.tooltips.tooltipCanvas.height-4); 
m_context.roundRect(2,2,this.tooltips.tooltipCanvas.width-4, this.tooltips.tooltipCanvas.height-4, 5, true, true); 
m_context.font="14px Arial"; 
m_context.fillStyle="RGBA(0,0,0,1)"; 
m_context.fillText("Index: " + (index + 1), 5, 20); 
m_context.fillText(this.series[index].name + ": " + this.series[index].value + this.unit, 5, 40); 
m_context.fillText(this.series[index].precent, 5, 60); 
m_context.restore(); 
// make tool-tip rectangle is always visible  
if((loc.x + this.tooltips.tooltipCanvas.width)> this.width) { 
loc.x = loc.x - this.tooltips.tooltipCanvas.width; 
} 
if((loc.y - this.tooltips.tooltipCanvas.height) <= 0) { 
loc.y = loc.y + this.tooltips.tooltipCanvas.height; 
} 
ctx.drawImage(this.tooltips.tooltipCanvas, 0, 0, this.tooltips.tooltipCanvas.width, this.tooltips.tooltipCanvas.height,  
loc.x, loc.y-this.tooltips.tooltipCanvas.height, this.tooltips.tooltipCanvas.width, this.tooltips.tooltipCanvas.height);	 
} else { 
this.tooltips.index = -1; 
this.clearTooltips(ctx); 
} 
}, 
clearTooltips : function(ctx) { 
ctx.clearRect(0,0,this.width, this.height); 
this.renderBorder(ctx); 
ctx.drawImage(this.animation.animCanvas, 0, 0, this.width, this.height, 0, 0, this.width, this.height);  
}, 
renderBorder : function(ctx) { 
ctx.save(); 
ctx.fillStyle="white"; 
ctx.strokeStyle="black"; 
ctx.fillRect(0, 0, this.width, this.height); 
ctx.strokeRect(0, 0, this.width, this.height); 
ctx.restore(); 
}, 
renderPie : function(ctx, index, precent, deltaArc) { 
var endAngle = deltaArc + 2*Math.PI*precent; 
ctx.beginPath(); 
ctx.arc(this.circle.cx, this.circle.cy, this.circle.radius, deltaArc, endAngle, false); 
ctx.moveTo(this.circle.cx, this.circle.cy); 
ctx.lineTo(this.circle.cx + this.circle.radius * Math.cos(deltaArc), this.circle.cy + this.circle.radius * Math.sin(deltaArc)); 
ctx.lineTo(this.circle.cx + this.circle.radius * Math.cos(endAngle), this.circle.cy + this.circle.radius * Math.sin(endAngle)); 
ctx.lineTo(this.circle.cx, this.circle.cy); 
ctx.closePath(); 
ctx.fillStyle = this.series[index].color; 
ctx.fill(); 
// render text content 
if(this.text.enable) {    		 
var halfEndAngle = deltaArc + Math.PI*precent; 
var hx = this.circle.cx + this.circle.radius * Math.cos(halfEndAngle); 
var hy = this.circle.cy + this.circle.radius * Math.sin(halfEndAngle); 
ctx.beginPath(); 
ctx.moveTo(hx, hy); 
var linePos = (hx < this.circle.cx) ? (hx - this.edge.width) : (hx + this.edge.width); 
ctx.lineTo(linePos, hy); 
ctx.closePath(); 
ctx.strokeStyle="black"; 
ctx.stroke(); 
var textPos = (hx < this.circle.cx) ? (hx - this.edge.width*2) : (hx + this.edge.width); 
precent = Math.round (precent*100) / 100; 
var size = this.text.content.length; 
var tipStr = (size > index) ? this.text.content[index] : this.series[index].name + ": " + (precent * 100).toFixed(0) + "%"; 
ctx.font = '10pt Calibri'; 
ctx.fillStyle="black"; 
ctx.fillText(tipStr, textPos, hy); 
} 
}, 
renderLegend : function(ctx, sum) { 
if(!this.legend.enable) return; 
var nums = this.series.length; 
ctx.font = '10pt Calibri'; 
var pos = (this.width/2 > (this.circle.radius+50)) ? 50 : (this.circle.cx - this.circle.radius); 
for(var i=0; i<nums; i++) { 
var x = this.series[i].value/sum; 
x = Math.round (x*100) / 100; 
var tipStr =  this.series[i].name + ": " + (x * 100).toFixed(0) + "%"; 
this.series[i].precent = tipStr; 
ctx.fillStyle = this.series[i].color; 
ctx.fillRect(pos - 40, 20*i+10, 10, 10); 
ctx.fillStyle = "black"; 
ctx.fillText(tipStr, pos - 25, 20*i+20); 
}    	 
}, 
playAnimation : function(parent) { 	 
if(parent.animation.hh < parent.height) { 
parent.animation.pctx.save(); 
parent.animation.pctx.globalAlpha=0.5; 
parent.animation.pctx.clearRect(0,0,parent.width, parent.height); 
parent.renderBorder(parent.animation.pctx); 
parent.animation.pctx.drawImage(parent.animation.animCanvas, 0, 0, parent.width, this.animation.hh, 0, 0, parent.width, this.animation.hh); 
parent.animation.hh = parent.animation.hh + 10; 
parent.animation.pctx.restore();    	    		 
setTimeout(function() {parent.playAnimation(parent);}, 1000/20);  
} else { 
parent.animation.pctx.clearRect(0,0,parent.width, parent.height); 
parent.renderBorder(parent.animation.pctx); 
parent.animation.pctx.drawImage(parent.animation.animCanvas, 0, 0, parent.width, parent.height, 0, 0, parent.width, parent.height); 
// enable tool-tip functionality 
if(parent.animation.enable && parent.legend.enable) { 
parent.chartCanvas.addEventListener('mousemove', function(event) { 
var x = event.pageX; 
var y = event.pageY; 
var canvas = event.target; 
var bbox = canvas.getBoundingClientRect(); 
var loc = { x: x - bbox.left * (canvas.width  / bbox.width), 
y: y - bbox.top  * (canvas.height / bbox.height)}; 
parent.showTooltips(loc, (parent.animation.enable ? parent.animation.pctx : ctx)); 
}, false); 
} 
} 
}, 
};

源代码可以直接使用

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/10089.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论