I am trying to create a custom HTML element that allows me to create pie charts easily. I have been very successful so far except for the fact that I cant seem to get the actual canvas drawing to be proportioned correctly.
class PieChart extends HTMLElement {
constructor(){
super();
}
connectedCallback(){
this.classList.add('pie-chart')
this.innerHTML = `<canvas style="width:100%; height:100%;"></canvas>`
this.style.display = 'block'
this.ctx = this.getElementsByTagName('canvas')[0].getContext('2d')
const results = [
{mood: "Angry", total: 1599, shade: "#0a9627"},
{mood: "Happy", total: 478, shade: "#960A2C"},
{mood: "Melancholic", total:332, shade: "#332E2E"},
{mood: "Gloomy", total: 195, shade: "#F73809"},
{mood: "Eh", total: 195, shade: "#000000"}
];
this.chartify(results);
}
chartify(results){
var el_width = this.getBoundingClientRect().width;
var half = (el_width / 2);
let sum = 0;
let portion = results.reduce((sum, {total}) => sum total, 0);
let currentAngle = 0;
for (let moodValue of results) {
//calculating the angle the slice (portion) will take in the chart
let portionAngle = (moodValue.total / portion) * 2 * Math.PI;
//drawing an arc and a line to the center to differentiate the slice from the rest
this.ctx.beginPath();
this.ctx.arc(half, half, half, currentAngle, currentAngle portionAngle);
currentAngle = portionAngle;
this.ctx.lineTo(half, half);
//filling the slices with the corresponding mood's color
this.ctx.fillStyle = moodValue.shade;
this.ctx.fill();
}
}
}
window.customElements.define('pie-chart', PieChart);
export{PieChart};
this is the returned result
The custom element is designed to have dynamic size. So to draw the arcs I was dividing the width by 2 in order to get the midpoint. I figured this would also be the radius. then all I need is to calculate the angle which I have no problem with. Why is it that the pie chart is oblong and not taking up the full space? What am I doing wrong in my canvas draw code?.
CodePudding user response:
The canvas element has default values for its height
and width
attributes of 150
and 300
, respectively. If you need your canvas to be square, you need to specify these sizes to be the same.
This example tweaks your el_width
variable to read the width off the canvas directly, instead of using getBoundingClientRect
. I've also tweaked the way the canvas
element is created so it has width
and height
attributes, and its 100% width and height styles are set via CSS:
class PieChart extends HTMLElement {
constructor(){
super();
}
connectedCallback(){
this.classList.add('pie-chart')
this.innerHTML = `<canvas width="300" height="300"></canvas>`
this.style.display = 'block'
this.ctx = this.getElementsByTagName('canvas')[0].getContext('2d')
const results = [
{mood: "Angry", total: 1599, shade: "#0a9627"},
{mood: "Happy", total: 478, shade: "#960A2C"},
{mood: "Melancholic", total:332, shade: "#332E2E"},
{mood: "Gloomy", total: 195, shade: "#F73809"},
{mood: "Eh", total: 195, shade: "#000000"}
];
this.chartify(results);
}
chartify(results){
var el_width = this.ctx.canvas.width;
var half = (el_width / 2);
let sum = 0;
let portion = results.reduce((sum, {total}) => sum total, 0);
let currentAngle = 0;
for (let moodValue of results) {
//calculating the angle the slice (portion) will take in the chart
let portionAngle = (moodValue.total / portion) * 2 * Math.PI;
//drawing an arc and a line to the center to differentiate the slice from the rest
this.ctx.beginPath();
this.ctx.arc(half, half, half, currentAngle, currentAngle portionAngle);
currentAngle = portionAngle;
this.ctx.lineTo(half, half);
//filling the slices with the corresponding mood's color
this.ctx.fillStyle = moodValue.shade;
this.ctx.fill();
}
}
}
window.customElements.define('pie-chart', PieChart);
pie-chart {
width: 150px;
aspect-ratio: 1 / 1;
}
pie-chart canvas {
width: 100%;
height: 100%;
}
<pie-chart></pie-chart>
You'll also notice that if you use width
and height
values that are smaller than the actual size at which the canvas is displayed, it will be noticeably scaled up since it's producing a raster image rather than a vector one.