I continue teaching JavaScript and JavaScript related libraries and frameworks. This time I’ll show how to pull user provided JSON data from a page and then create a randomly generated pie chart using Canvas. Along the way we’ll learn how to randomly generate color hex codes, parse JSON data, explode pie charts, and the math behind drawing on canvas.
All of the code with a transcript of the video follows below to help you learn.
This 32 minute tutorial only has one 5 second skippable ad. Please consider not Ad Blocking it so that I can continue making free tutorials.
Code From the Video
Jstut3.html
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<meta name="viewport" content="width = device-width, initial-scale = 1">
<title>Pie Chart Creater</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="mainstyle.css">
</head>
<body>
<div id="container">
<div id="jumbotron">
<canvas id='canvas' width='600' height='600'>
</canvas><br>
<textarea id="json-data"></textarea><br>
<button type="submit" class="btn-lg mybut" onClick="drawChart()">Create Pie Chart</button>
</div>
</div>
<script src="jstut3.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
Mainstyle.css
body{
background: #000000;
}
#canvas{
margin: 10px;
padding: 10px;
background: #ffffff;
border: thin inset #000000;
}
#json-data{
margin: 10px;
padding: 10px;
width: 620px;
height: 200px;
background: #ffffff;
border: thin inset #000000;
}
.mybut{
margin: 10px;
padding: 10px;
width: 620px;
}
Jstut3.js
// ----- DRAW PIE CHART -----
// This is how you structure JSON data
// var data = '{"expenditures":[{"type":"Healthcare", "percent":"8"},{"type":"Insurance", "percent":"12"},{"type":"Food", "percent":"13"},{"type":"Housing", "percent":"33"},{"type":"Transportation", "percent":"16"},{"type":"Apparel", "percent":"3"},{"type":"Entertainment", "percent":"5"},{"type":"Healthcare", "percent":"8"},{"type":"Other", "percent":"2"}]}';
// This data is structured as an array of expenditure objects
// like this
// class Expenditures {
// constructor(type, percent) {
// this.type = type,
// this.percent = percent;
// }
// }
// Will hold JSON data pulled from the textarea
let data;
// Will hold array of expenditures
let expenditureArray = [];
// Will hold array of percentages converted so they can
// be used to draw the pie chart
let percentArray = [];
// Will hold randomly generated color hex codes
let colorArray = [];
function drawChart(){
// Get data pasted into textarea
data = document.getElementById("json-data").value;
// Will hold the calculated percentages of the pie
// each category will take up
percentArray = createPercentArray();
// Will store an array of random color hex codes
colorArray = createRandomColorArray();
// Will convert JSON data into an array of objects
populateArray(data);
// Draws pie chart and lables
drawPie();
}
// Pulls data from JSON and saves it into array
// expenditureArray
function populateArray(jsonData){
// Create an array populated with expense data
let expenseArray = JSON.parse(jsonData);
// Cycle through data and put it in our array
for(i = 0; i < expenseArray.expenditures.length; i++){
// Get each object in the array
let expense = expenseArray.expenditures[i];
expenditureArray[i] = expense;
}
}
// Create an array where we convert our percentages so
// that they total up to 2 [100 * .02 = 2]
function createPercentArray(){
let perArr = [];
for(i = 0; i < expenditureArray.length; i++){
perArr[i] = expenditureArray[i].percent * .02;
}
return perArr;
}
function createRandomColorArray(){
let randColorArr = [];
for(i = 0; i < expenditureArray.length; i++){
// Generate random base 16 hex code
// 16777215 = FFFFFF
// toString(16) returns base 16 of provided number
randColorArr[i] = '#' + Math.floor(Math.random() * 16777215).toString(16);
}
return randColorArr;
}
function drawPie(){
// Get reference to canvas
var canvas = document.getElementById('canvas');
// Gives you the ability to draw 2D shapes, text,
// images, etc.
var context = canvas.getContext('2d');
// Start drawing arc at 0
let startAngle = 0;
let endAngle = 0;
for(i = 0; i < percentArray.length; i++){
// Start the next arc where the last arc left off
startAngle = endAngle;
endAngle = endAngle + (percentArray[i] * Math.PI);
drawSlice(context, 300, 200, 150, startAngle,
endAngle, colorArray[i]);
// Have to multiply by 50 because original data
// was multiplied times .02
drawSliceText(context, 300, 200, 150, startAngle,
endAngle, percentArray[i]*50);
}
}
function drawSlice(ctx, sliceCenterX, sliceCenterY, radius,
startAngle, endAngle, color){
// fillStyle : Color, Gradient or Pattern used to fill shapes
ctx.fillStyle = color;
// Signal that you want to start drawing a path
ctx.beginPath();
// ----- Code used to separate slices -----
// Get median angle
let medianAngle = (startAngle + endAngle) / 2;
// Get offset amount from center
// Increace multiple to increase offset
xOffset = Math.cos(medianAngle) * 30;
yOffset = Math.sin(medianAngle) * 30;
// Define start of drawing area
// Delete offsets to have a normal pie chart
ctx.moveTo(sliceCenterX + xOffset, sliceCenterY + yOffset);
// Draw arc for pie slice
ctx.arc(sliceCenterX + xOffset, sliceCenterY + yOffset, radius, startAngle, endAngle);
// Close the pie slice
ctx.closePath();
// Fill the pie slice
ctx.fill();
}
function drawSliceText(ctx, sliceCenterX, sliceCenterY, radius,
startAngle, endAngle, percentText){
// Draw Text
// Get Median Angle : (startAngle + endAngle)/2
// I define where to place text using polar coordinates
// They are defined in Cartesian coordinates using
// x = radius * cos(angular coordinate)
// y = radius * sin(angular coordinate)
let textX = sliceCenterX + Math.cos((startAngle + endAngle)/2) * radius;
let textY = sliceCenterY + Math.sin((startAngle + endAngle)/2) * radius;
ctx.fillStyle = 'black';
ctx.font = '24px Calibri';
ctx.fillText(percentText, textX, textY);
}