Cara menggunakan html charts without javascript
Recently I was working on a project to improve our web app’s billing reports with pie charts that visualize customer usage. I wanted an implementation for the job with minimal foreseeable maintenance that kept our third-party dependencies low.
I thought a quick search on Stack Overflow would yield a copy/paste-able solution, but discovered there isn’t an agreed-upon approach.
Of the various approaches I saw, there are these three that caught my attention, and they all happen to use just HTML and CSS. Each approach has its own merits, and you’ll need to find the right approach for your use case.
Here’s a quick summary of what the implementation of the three approaches looks like:
Using CSS conic-gradient
function
//HTML
// CSS
#shape {
width: 300px;
height: 300px;
border-radius: 9999px;
background: conic-gradient[
LemonChiffon 0%, LemonChiffon 10%,
LightGreen 10%, LightGreen 30%,
WhiteSmoke 30%, WhiteSmoke 100%
];
}
Preview code snippet
Using SVG
element
// HTML
// CSS
#shape svg {
width: 300px;
height: 300px;
border-radius: 9999px;
background-color: WhiteSmoke;
}
Preview code snippet
Using SVG
//HTML
// CSS
#shape svg {
width: 300px;
height: 300px;
border-radius: 9999px;
background-color: WhiteSmoke;
}
Preview code snippet
Each approach has its pros and cons, and offers different types of versatility:
conic-gradient
approach allows you to not deal with SVG at all. Very little maths is needed but at the cost of slightly less browser compatibility compared to the other solutions.- The
element solution requires you to understand how all of its parameters work. But once you’ve got those figured out, you can stack and control pie pieces individually. You can also use the same implementation to turn your pie charts into donut charts. - The
element solution requires you to understand how to calculate paths, so you need to spend some time learning the basics of SVG. But once you’re comfortable with plotting, you aren’t restricted to the shapes you want to draw. This solution allows highest customization to your chart, like giving the pie slices borders or using background images for each slice.
The SVG
element solution came out on top for me and for our use case: we currently don’t need chart animations/interactions and having the same implementation for both pie charts and donut charts is pretty useful.
The important thing to know about the
solution is that it takes advantage of drawing overlapping circles [one for each segment of the pie chart] and that we use the `dash` feature of SVG to draw each segment. Dashes are normally used to define how to represent breaks in lines, but you can also use them in other SVG objects like circles, and by defining the dashes carefully we can have one dash per circle to generate each slice of the pie chart.
Here’s a quick overview of how the
example above works.
- Start by defining the
// CSS
#shape {
width: 300px;
height: 300px;
border-radius: 9999px;
background: conic-gradient[
LemonChiffon 0%, LemonChiffon 10%,
LightGreen 10%, LightGreen 30%,
WhiteSmoke 30%, WhiteSmoke 100%
];
}
6 to be
// CSS
#shape {
width: 300px;
height: 300px;
border-radius: 9999px;
background: conic-gradient[
LemonChiffon 0%, LemonChiffon 10%,
LightGreen 10%, LightGreen 30%,
WhiteSmoke 30%, WhiteSmoke 100%
];
}
7 to simplify the calculation, fits a circle of 50 units radius. You can use
// CSS
#shape {
width: 300px;
height: 300px;
border-radius: 9999px;
background: conic-gradient[
LemonChiffon 0%, LemonChiffon 10%,
LightGreen 10%, LightGreen 30%,
WhiteSmoke 30%, WhiteSmoke 100%
];
}
8 and
// CSS
#shape {
width: 300px;
height: 300px;
border-radius: 9999px;
background: conic-gradient[
LemonChiffon 0%, LemonChiffon 10%,
LightGreen 10%, LightGreen 30%,
WhiteSmoke 30%, WhiteSmoke 100%
];
}
9 to define the size of the pie chart. The
// CSS
#shape {
width: 300px;
height: 300px;
border-radius: 9999px;
background: conic-gradient[
LemonChiffon 0%, LemonChiffon 10%,
LightGreen 10%, LightGreen 30%,
WhiteSmoke 30%, WhiteSmoke 100%
];
}
6 is the canvas we’re going to be drawing on. - For the circles you want to draw, you set the radius value to the half of the outcome circle’s radius [that is
// HTML
1 instead of 50] and set the
// HTML
2 to be half the length of the
// CSS
#shape {
width: 300px;
height: 300px;
border-radius: 9999px;
background: conic-gradient[
LemonChiffon 0%, LemonChiffon 10%,
LightGreen 10%, LightGreen 30%,
WhiteSmoke 30%, WhiteSmoke 100%
];
}
6 [that is
// HTML
4]. By doing this, you will be drawing border of the circle with thickness equals to the length of the outcome circle’s radius, thus creating a solid circle. - Use
// HTML
5 to set the dash length and gap size between strokes. - The dash length will be the percentage of the circumference. For example, to draw 10% pie, it will be
// HTML
6 - To ensure the border dash appears only once set the gap size to be the circumference minus the dash length [that is
// HTML
7]. - To draw the first slice of 10% use the percentage of the circumference and the gap size we just calculated with the expression
// HTML
8 - Rotate the starting point from the default
// HTML
9 by 90 degrees or 25% of the circumference
// CSS
#shape svg {
width: 300px;
height: 300px;
border-radius: 9999px;
background-color: WhiteSmoke;
}
0. - If the pie is not starting at the 0% mark, then take the percentage of rotation into the calculation. Example, to start at 10% mark, the
// CSS
#shape svg {
width: 300px;
height: 300px;
border-radius: 9999px;
background-color: WhiteSmoke;
}
1 will be
// CSS
#shape svg {
width: 300px;
height: 300px;
border-radius: 9999px;
background-color: WhiteSmoke;
}
2. - To fill up any of the remaining area left, apply
// CSS
#shape svg {
width: 300px;
height: 300px;
border-radius: 9999px;
background-color: WhiteSmoke;
}
3 and
// CSS
#shape svg {
width: 300px;
height: 300px;
border-radius: 9999px;
background-color: WhiteSmoke;
}
4 on
// CSS
#shape svg {
width: 300px;
height: 300px;
border-radius: 9999px;
background-color: WhiteSmoke;
}
5 element.
We can implement this as a React component to make it easier for other contributors to reuse:
const CIRCLE_RADIUS = 50 / 2;
const STROKE_WIDTH = CIRCLE_RADIUS * 2;
const CIRCUMFERENCE = 2 * Math.PI * CIRCLE_RADIUS;
const DASH_OFFSET = CIRCUMFERENCE / 4;
const PieChart = [{
backgroundColor = 'WhiteSmoke',
strokeColorOptions = ['LemonChiffon', 'LightGreen'],
values = [],
}] => {
const outputStrokeColors = _.times[values.length].map[
[i] => strokeColorOptions[i % strokeColorOptions.length]
];
return [
{values.map[[val, index] => {
const strokeColor =
outputStrokeColors[index % outputStrokeColors.length];
const dashSize = [val / 100] * CIRCUMFERENCE;
const gapSize = CIRCUMFERENCE - dashSize;
// NOTE: if it's a full circle, then no need to provide a stroke dasharray
const strokeDasharray =
val === 100 ? 'none' : `${dashSize} ${gapSize}`;
const accumulatedPriorPercentage = _.sum[values.slice[0, index]];
const relativeOffset =
[accumulatedPriorPercentage / 100] * CIRCUMFERENCE;
const adjustedOffset = DASH_OFFSET - relativeOffset;
return [
];
}]}
];
};
There might be a point where requirements change and we’ll need to adopt another solution or revisit existing libraries. Until then, the lightweight and in-scope versatility of the SVG
solution will serve us well.