How to draw a multi level pie chart using html5 canvas? - javascript

I'm trying to make a pie-chart shape on a canvas element, however I can't find any function that does this by itself.
Is there an easy way to do a pie chart like below image
?

Here's a starting example for you that draws arcs using a recallable function:
Remember that the starting & ending arc angles are Radians. You can convert degrees to radians like this: radians = degrees * Math.PI/180
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
ctx.lineWidth = 2;
ctx.font = '14px verdana';
var PI2 = Math.PI * 2;
var myColor = ["Green", "Red", "Blue"];
var myData = [30, 15, 38, 22, 30, 20, 10];
var cx = 150;
var cy = 150;
var radius = 100;
ctx.globalAlpha=0.50;
pieChart(myData, myColor);
ctx.globalAlpha=1.00;
function pieChart(data, colors){
// calc data total
var total = 0;
for (var i = 0; i < data.length; i++) {
total += data[i];
}
// calc sweep angles for each piece of pie
var sweeps = []
for (var i = 0; i < data.length; i++) {
sweeps.push(data[i] / total * PI2);
}
// draw outer pie
var accumAngle = 0;
for (var i = 0; i < sweeps.length; i++) {
var f=randomColor();
drawWedge(radius, accumAngle, accumAngle + sweeps[i], f, data[i]);
accumAngle += sweeps[i];
}
// draw inner percent-complete wedges
var accumAngle = 0;
for (var i = 0; i < sweeps.length; i++) {
var r=radius*(Math.random()*70+20)/100;
var f=randomColor();
drawWedge(r,accumAngle, accumAngle + sweeps[i], f, data[i]);
accumAngle += sweeps[i];
}
}
function drawWedge(radius, startAngle, endAngle, fill, label) {
// draw the wedge
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, radius, startAngle, endAngle, false);
ctx.closePath();
ctx.fillStyle = fill;
ctx.strokeStyle = 'white';
ctx.fill();
ctx.stroke();
}
function randomColor(){
return('#'+(Math.floor(Math.random() * 0x1000000) + 0x1000000).toString(16).substr(1));
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<canvas id="canvas" width=512 height=512></canvas>

Related

chessboard canvas , loop and get some checkers as a circles in JS canvas

I am struggling how to create multiple checkers on the checkerboard
I have this JS code so far:
<script>
$(document).ready(function() {
var gColor = "black";
var c = $("#myCanvas")[0];
var ctx = c.getContext("2d");
ctx.lineWidth = 2;
ctx.strokeStyle = "black"; //set the color, gradient, or pattern for stroke
drawBoard();
$("#color").click(function () {
gColor = $("#color").val();
if (gColor > "")
drawBoard();
});
function drawBoard() {
var x, y, step = 60, step2 = 120;
ctx.rect(0,0,480,480);
ctx.stroke();
ctx.save();
for (var k=0; k<2; k++) {
step2 -= step;
ctx.translate(0, step*k);
console.log("translate:("+0+","+step*k+")");
for (var y=0; y<4; y++) {
for (var i=0; i<4; i++) {
x = i * 2 * step + step2;
ctx.fillStyle = gColor;
ctx.fillRect(x,y*step*2,step,step);
console.log("fillRect: "+x+","+y*step*2+","+step+","+step+","+gColor);
}
}
}
ctx.restore();
}
var c = $("#myCanvas")[0];
var ctx = c.getContext("2d");
var rad = 90;
for (var i=0; i<1; i++) {
switch (i) {
case 0: ctx.fillStyle = "red";
break;
}
ctx.beginPath();
// Draw circle (x, y, radius, starting angle, ending angle in radian)
ctx.arc(30, 30, 30, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
}
});
</script>
I understand that I need to do some sort of looping, but because I am just learning canvas I don't know where to start. Any help or advice appreciated
for (var i=0; i<1; i++) {
switch (i) {
case 0: ctx.fillStyle = "red";
break;
}
ctx.beginPath();
// Draw circle (x, y, radius, starting angle, ending angle in radian)
ctx.arc(30, 30, 30, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
The Circle you are drawing is outside the for loop and has hardcoded values of 30 as x and 30 as Y
You need to use the same logic that you have used for creating the black squares to create circles as well
Your Squares are 60, 60 so,
var size = 60;
var rows = 8;
var cols = 8;
for (let i=0; i < rows; i++) {
for (let j=0; j < cols; j++) {
// J is the column Identifier
// I is the row identifier
var x = j*size + (size/2); // j*size takes the center of cicle at the begining of current square + size/2 makes it at the exact center of square
var y = i*size + (size/2); // i*size takes the center of circle at the top of current square, same to bring vertical center size/2 is added
ctx.beginPath();
// Draw circle at (x, y, radius, starting angle, ending angle in radian)
ctx.arc(x, y, (size/2), 0, 2 * Math.PI); // radius is half of square width
ctx.fill();
ctx.stroke();
}
}
This would draw Circles at every square.
The problem is not with canvas, but simple logic of iterating and calculating x and y positions of a circle. I think you should read up a bit more on loops which will help you further.

Canvas line drawing animation

I am new learner of animation using HTML5 Canvas. I am struggling to create line drawing animation in a canvas with desired length of a line.
Here is the code
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
width = canvas.width = window.innerWidth,
height = canvas.height = window.innerHeight;
var x = 200;
var y = 200;
draw();
update();
function draw() {
context.beginPath();
context.moveTo(100, 100);
context.lineTo(x, y);
context.stroke();
}
function update() {
context.clearRect(0, 0, width, height);
x = x + 1;
y = y + 1;
draw();
requestAnimationFrame(update);
}
html,
body {
margin: 0px;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
The line is growing on Canvas in the above code. But how to achieve that the 200px wide line and animate the movement in x and y direction. And the same animation with multiple lines using for loop and move them in different direction.
Check the reference image ....
Need to move each line in a different direction
Thanks in advance
Find a new reference image which i want to achieve
You need to either use transforms or a bit of trigonometry.
Transforms
For each frame:
Reset transforms and translate to center
Clear canvas
Draw line from center to the right
Rotate x angle
Repeat from step 2 until all lines are drawn
var ctx = c.getContext("2d");
var centerX = c.width>>1;
var centerY = c.height>>1;
var maxLength = Math.min(centerX, centerY); // use the shortest direction for demo
var currentLength = 0; // current length, for animation
var lenStep = 1; // "speed" of animation
function render() {
ctx.setTransform(1,0,0,1, centerX, centerY);
ctx.clearRect(-centerX, -centerY, c.width, c.height);
ctx.beginPath();
for(var angle = 0, step = 0.1; angle < Math.PI * 2; angle += step) {
ctx.moveTo(0, 0);
ctx.lineTo(currentLength, 0);
ctx.rotate(step);
}
ctx.stroke(); // stroke all at once
}
(function loop() {
render();
currentLength += lenStep;
if (currentLength < maxLength) requestAnimationFrame(loop);
})();
<canvas id=c></canvas>
You can use transformation different ways, but since you're learning I kept it simple in the above code.
Trigonometry
You can also calculate the line angles manually using trigonometry. Also here you can use different approaches, ie. if you want to use delta values, vectors or brute force using the math implicit.
For each frame:
Reset transforms and translate to center
Clear canvas
Calculate angle and direction for each line
Draw line
var ctx = c.getContext("2d");
var centerX = c.width>>1;
var centerY = c.height>>1;
var maxLength = Math.min(centerX, centerY); // use the shortest direction for demo
var currentLength = 0; // current length, for animation
var lenStep = 1; // "speed" of animation
ctx.setTransform(1,0,0,1, centerX, centerY);
function render() {
ctx.clearRect(-centerX, -centerY, c.width, c.height);
ctx.beginPath();
for(var angle = 0, step = 0.1; angle < Math.PI * 2; angle += step) {
ctx.moveTo(0, 0);
ctx.lineTo(currentLength * Math.cos(angle), currentLength * Math.sin(angle));
}
ctx.stroke(); // stroke all at once
}
(function loop() {
render();
currentLength += lenStep;
if (currentLength < maxLength) requestAnimationFrame(loop);
})();
<canvas id=c></canvas>
Bonus animation to play around with (using the same basis as above):
var ctx = c.getContext("2d", {alpha: false});
var centerX = c.width>>1;
var centerY = c.height>>1;
ctx.setTransform(1,0,0,1, centerX, centerY);
ctx.lineWidth = 2;
ctx.strokeStyle = "rgba(0,0,0,0.8)";
ctx.shadowBlur = 16;
function render(time) {
ctx.globalAlpha=0.77;
ctx.fillRect(-500, -500, 1000, 1000);
ctx.globalAlpha=1;
ctx.beginPath();
ctx.rotate(0.025);
ctx.shadowColor = "hsl(" + time*0.1 + ",100%,75%)";
ctx.shadowBlur = 16;
for(var angle = 0, step = Math.PI / ((time % 200) + 50); angle < Math.PI * 2; angle += step) {
ctx.moveTo(0, 0);
var len = 150 + 150 * Math.cos(time*0.0001618*angle*Math.tan(time*0.00025)) * Math.sin(time*0.01);
ctx.lineTo(len * Math.cos(angle), len * Math.sin(angle));
}
ctx.stroke();
ctx.globalCompositeOperation = "lighter";
ctx.shadowBlur = 0;
ctx.drawImage(ctx.canvas, -centerX, -centerY);
ctx.drawImage(ctx.canvas, -centerX, -centerY);
ctx.globalCompositeOperation = "source-over";
}
function loop(time) {
render(time);
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
body {margin:0;background:#222}
<canvas id=c width=640 height=640></canvas>
Here is what I think you are describing...
window.onload = function() {
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
width = canvas.width = 400,
height = canvas.height = 220,
xcenter = 200,
ycenter = 110,
radius = 0,
radiusmax = 100,
start_angle1 = 0,
start_angle2 = 0;
function toRadians(angle) {
return angle * (Math.PI / 180);
}
function draw(x1, y1, x2, y2) {
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
}
function drawWheel(xc, yc, start_angle, count, rad) {
var inc = 360 / count;
for (var angle = start_angle; angle < start_angle + 180; angle += inc) {
var x = Math.cos(toRadians(angle)) * rad;
var y = Math.sin(toRadians(angle)) * rad;
draw(xc - x, yc - y, xc + x, yc + y);
}
}
function update() {
start_angle1 += 0.1;
start_angle2 -= 0.1;
if(radius<radiusmax) radius++;
context.clearRect(0, 0, width, height);
drawWheel(xcenter, ycenter, start_angle1, 40, radius);
drawWheel(xcenter, ycenter, start_angle2, 40, radius);
requestAnimationFrame(update);
}
update();
};
html,
body {
margin: 0px;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
This is one that is a variable length emerging pattern. It has a length array element for each spoke in the wheel that grows at a different rate. You can play with the settings to vary the results:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = canvas.width = window.innerWidth;
var height = canvas.height = window.innerHeight;
var xcenter = width/4;
var ycenter = height/2;
var radius;
var time;
if(width>height) {
radius = height*0.4;
}
else {
radius = width*0.4;
}
var start_angle1 = 0;
var start_angle2 = 0;
function toRadians (angle) {
return angle * (Math.PI / 180);
}
function draw(x1,y1,x2,y2) {
context.beginPath();
context.moveTo(x1,y1);
context.lineTo(x2,y2);
context.stroke();
}
var radmax=width;
var rads = [];
var radsinc = [];
function drawWheel(xc,yc,start_angle,count,rad) {
var inc = 360/count;
var i=0;
for(var angle=start_angle; angle < start_angle+180; angle +=inc) {
var x = Math.cos(toRadians(angle)) * rads[rad+i];
var y = Math.sin(toRadians(angle)) * rads[rad+i];
draw(xc-x,yc-y,xc+x,yc+y);
rads[rad+i] += radsinc[i];
if(rads[rad+i] > radmax) rads[rad+i] = 1;
i++;
}
}
function update() {
var now = new Date().getTime();
var dt = now - (time || now);
time = now;
start_angle1 += (dt/1000) * 10;
start_angle2 -= (dt/1000) * 10;
context.clearRect(0,0,width,height);
drawWheel(xcenter,ycenter,start_angle1,50,0);
drawWheel(xcenter,ycenter,start_angle2,50,50);
requestAnimationFrame(update);
}
function init() {
for(var i=0;i<100;i++) {
rads[i] = 0;
radsinc[i] = Math.random() * 10;
}
}
window.onload = function() {
init();
update();
};
html, body {
margin: 0px;
}
canvas {
width:100%;
height:200px;
display: block;
}
<canvas id="canvas"></canvas>

how to add label to the chart created using canvas

hello all i am able to display the chart but i need to add labels to the chart the labels are given below but i am not able to display it in the chart any help pls thanks in advance
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var lastend = 0;
var data = [20,40]; // If you add more data values make sure you add more colors
var labels = ["leavebalance", "leaveavailability"];
var myTotal = 0; // Automatically calculated so don't touch
var myColor = ['red','green']; // Colors of each slice
for (var e = 0; e < data.length; e++) {
myTotal += data[e];
}
//alert(myTotal);
for (var i = 0; i < data.length; i++) {
ctx.fillStyle = myColor[i];
//ctx.fillText(labels[i]);
//ctx.fillText = labels[i];
ctx.beginPath();
ctx.moveTo(canvas.width / 2, canvas.height / 2);
// Arc Parameters: x, y, radius, startingAngle (radians), endingAngle (radians), antiClockwise (boolean)
ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2, lastend, lastend + (Math.PI * 2 * (data[i] / myTotal)), false);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fill();
lastend += Math.PI * 2 * (data[i] / myTotal);
}
<canvas id="canvas" width="200" height="150" >
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
Here's one way to apply labels to each wedge in a pie chart:
Calculate the middle angle between the starting & ending angles for each wedge in the pie.
Calculate the [x,y] that is on the middle angle and near the circumference.
Set textAlign & textBaseline so drawn text is centered on the calculated [x,y].
Draw the label at the calculated [x,y] fillText('20',x,y)
Here's annotated code and a Demo:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
ctx.lineWidth = 2;
ctx.font = '12px verdana';
ctx.textAlign='center';
ctx.textBaseline='middle';
var PI2 = Math.PI * 2;
var myColor = ['red','green','blue'];
var myData = [25,35,40];
var cx = 150;
var cy = 150;
var radius = 100;
pieChart(myData, myColor);
function pieChart(data, colors) {
var total = 0;
for (var i = 0; i < data.length; i++) {
total += data[i];
}
var sweeps = []
for (var i = 0; i < data.length; i++) {
sweeps.push(data[i] / total * PI2);
}
var accumAngle = 0;
for (var i = 0; i < sweeps.length; i++) {
drawWedge(accumAngle, accumAngle + sweeps[i], colors[i], data[i]);
accumAngle += sweeps[i];
}
}
function drawWedge(startAngle, endAngle, fill, label) {
// draw the wedge
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, radius, startAngle, endAngle, false);
ctx.closePath();
ctx.fillStyle = fill;
ctx.strokeStyle = 'black';
ctx.fill();
ctx.stroke();
// draw the label
var midAngle = startAngle + (endAngle - startAngle) / 2;
var labelRadius = radius * .75;
var x = cx + (labelRadius) * Math.cos(midAngle);
var y = cy + (labelRadius) * Math.sin(midAngle);
ctx.fillStyle = 'white';
ctx.fillText(label, x, y);
}
body {
background-color: ivory;
padding: 10px;
}
#canvas {
border: 1px solid red;
}
<canvas id="canvas" width=300 height=300></canvas>

How I can set text to canvas circle

I used this question and I create shape like this and but now I don't know how I can set text to each circle in just first time click? (like tic tac toe)
Here you go! - I merged it for ease. Just click on circle to see text on it.
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var radius = 70;
var lineWidth = 5;
var cols = 3;
var rows = 2;
var distance = 50;
var circles = [];
//click circle to write
canvas.onclick = function(e) {
// correct mouse coordinates:
var rect = canvas.getBoundingClientRect(), // make x/y relative to canvas
x = e.clientX - rect.left,
y = e.clientY - rect.top,
i = 0, circle;
// check which circle:
while(circle = circles[i++]) {
context.beginPath(); // we build a path to check with, but not to draw
context.arc(circle.x, circle.y, circle.radius, 0, 2*Math.PI);
if (context.isPointInPath(x, y) && !circle.clicked) {
circle.clicked = true;
context.fillStyle = "blue";
context.font = "bold 34px Arial";
context.textAlign="center";
context.fillText("Yeah", circle.x, circle.y);
break;
}
}
};
//draw circles
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
// Draw circle
var offset = radius * 2 + lineWidth + distance;
var center = radius + lineWidth;
var x = j * offset + center;
var y = i * offset + center;
circles.push({
id: i + "," + j, // some ID
x: x,
y: y,
radius: radius,
clicked:false
});
console.log(circles)
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
context.lineWidth = lineWidth;
context.strokeStyle = '#003300';
if (j != cols - 1) {
// Draw horizontal line
var hLineX = x + radius;
var hLineY = y;
context.moveTo(hLineX, hLineY);
context.lineTo(hLineX + distance + lineWidth, hLineY);
}
if (i > 0) {
// Draw vertical line
var vLineY = y - radius - distance - lineWidth;
context.moveTo(x, vLineY);
context.lineTo(x, vLineY + distance + lineWidth);
}
context.stroke();
}
}
<div id="ways" style="width:1000px;margin:0 auto;height:100%;">
<canvas id="canvas" width="1000" height="1000"></canvas>
</div>
Happy Helping!

HTML5 Canvas Arc redraw on hover

I have three arcs, the first one loads on page-load, the second one loads on mouse-over and the third one on mouse-out. I want the mouse-over-out effect to happen each time rather than just one time (as it is now).
here's the fiddle: http://jsfiddle.net/krish7878/7bX7n/
Here's the JS code:
var currentEndAngle = 0;
var currentStartAngle = 0;
var currentEndAngle2 = 0;
var currentStartAngle2 = 0;
var currentEndAngle3 = -0.5;
var currentStartAngle3 = -0.5;
var something = setInterval(draw, 5);
$("#canvas1").hover(
function(){
var something2 = setInterval(draw2, 5);
},
function(){
var something3 = setInterval(draw3, 5);
}
);
function draw() { /***************/
var can = document.getElementById('canvas1'); // GET LE CANVAS
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius;
var width;
var currentColor = "#00b5ff";
var radius = 100;
var width = 8;
var startAngle = currentStartAngle * Math.PI;
var endAngle = (currentEndAngle) * Math.PI;
if(currentEndAngle < 0.1){
currentEndAngle = currentEndAngle - 0.01;
}
if (currentEndAngle < -0.5){
clearInterval(something);
}
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, true);
context.lineWidth = width;
// line color
context.strokeStyle = currentColor;
context.stroke();
/************************************************/
}
function draw2() { /***************/
var can = document.getElementById('canvas1'); // GET LE CANVAS
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius;
var width;
var currentColor = "#000";
var radius = 100;
var width = 7;
var startAngle = currentStartAngle2 * Math.PI;
var endAngle = (currentEndAngle2) * Math.PI;
if(currentEndAngle2 < 0.1){
currentEndAngle2 = currentEndAngle2 - 0.01;
}
if (currentEndAngle2 < -0.55){
clearInterval(something2);
}
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, true);
context.lineWidth = width;
// line color
context.strokeStyle = currentColor;
context.stroke();
/*
context.beginPath();
context.clearRect ( 0 , 0 , 400 , 400 );
context.stroke():
/************************************************/
}
function draw3() { /***************/
var can = document.getElementById('canvas1'); // GET LE CANVAS
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius;
var width;
var currentColor = "#00b5ff";
var radius = 100;
var width = 8;
var startAngle = currentStartAngle3 * Math.PI;
var endAngle = (currentEndAngle3) * Math.PI;
if(currentEndAngle3 < 0){
currentEndAngle3 = currentEndAngle3 + 0.01;
}
if (currentEndAngle3 > 0){
clearInterval(something3);
}
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, false);
context.lineWidth = width;
// line color
context.strokeStyle = currentColor;
context.stroke();
/************************************************/
}
Code Explanation: there are three functions draw(), draw2(), draw3() - draw is run when the page loads, it draws a blue arc, draw2() is executed when mouse-over happens and draws a black line, draw3 is run when mouse-out happens.
Show I draw them on individual canvases and clear them individually or is there a method to get this done?
Here's one way to do it:
A Demo: http://jsfiddle.net/m1erickson/wMy4G/
Define an arc object
var arc={
cx:canvas.width/2,
cy:canvas.height/2,
radius:100,
startRadians:0,
endRadians:-Math.PI/2,
linewidth:8,
animationPercent:0,
animationRate:10,
animationDirection:0,
};
Draw a portion of the arc based on an animation point
function drawArc(arc,color){
var rStart=arc.startRadians;
var rEnd=arc.endRadians;
if(!arc.animationDirection==0){
if(arc.animationDirection>0){
rEnd=arc.animationPercent/100*(rEnd-rStart);
}else{
rEnd=(100-arc.animationPercent)/100*(rEnd-rStart);
}
}
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.arc(arc.cx,arc.cy,arc.radius,rStart,rEnd,true);
ctx.strokeStyle=color;
ctx.stroke();
}
Animate portions of the arc
function animate(time){
if(continueAnimation){RAF=requestAnimationFrame(animate);}
drawArc(arc,"blue");
arc.animationPercent+=arc.animationRate;
if(arc.animationPercent>=100){
continueAnimation=false;
}
}
React to hover events by drawing or undrawing the arc
$("#canvas").hover(
function(){
cancelAnimationFrame(RAF);
arc.animationPercent=0;
arc.animationDirection=1;
continueAnimation=true;
requestAnimationFrame(animate);
},
function(){
cancelAnimationFrame(RAF);
arc.animationPercent=0;
arc.animationDirection=-1;
continueAnimation=true;
requestAnimationFrame(animate);
}
);

Categories

Resources