{"id":350,"date":"2022-08-30T23:08:00","date_gmt":"2022-08-30T23:08:00","guid":{"rendered":"http:\/\/rainforestqa.com\/drawing-pie-charts-without-javascript\/"},"modified":"2024-07-08T22:59:54","modified_gmt":"2024-07-08T22:59:54","slug":"drawing-pie-charts-without-javascript","status":"publish","type":"post","link":"https:\/\/www.rainforestqa.com\/blog\/drawing-pie-charts-without-javascript","title":{"rendered":"Three ways to draw pie charts without any JavaScript"},"content":{"rendered":"\n<p>Recently I was working on a project to improve our web app\u2019s 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.<\/p>\n\n\n\n<p>I thought a quick search on Stack Overflow would yield a copy\/paste-able solution, but discovered there isn\u2019t an agreed-upon approach.<\/p>\n\n\n\n<p>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\u2019ll need to find the right approach for your use case.<\/p>\n\n\n\n<p>Here\u2019s a quick summary of what the implementation of the three approaches looks like:<\/p>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.rainforestqa.com\/blog\/drawing-pie-charts-without-javascript\/#Using_CSS_conic-gradient_function\" >Using CSS\u00a0conic-gradient function<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.rainforestqa.com\/blog\/drawing-pie-charts-without-javascript\/#Using_SVG_element\" >Using SVG\u00a0&lt;circle>\u00a0element<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.rainforestqa.com\/blog\/drawing-pie-charts-without-javascript\/#Using_SVG\" >Using SVG\u00a0&lt;path><\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Using_CSS_conic-gradient_function\"><\/span>Using CSS\u00a0<code>conic-gradient<\/code> function<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/HTML\n&lt;div id=\"shape\">\n&lt;\/div><\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ CSS\n#shape {\n width: 300px;\n height: 300px;\n border-radius: 9999px;\n background: conic-gradient(\n  LemonChiffon 0%, LemonChiffon 10%,\n  LightGreen 10%, LightGreen 30%,\n  WhiteSmoke 30%, WhiteSmoke 100%\n );\n}<\/code><\/pre>\n\n\n\n<p><a id=\"\" rel=\"noopener\" href=\"https:\/\/jsfiddle.net\/9k7m83as\/\" target=\"_blank\">Preview code snippet<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Using_SVG_element\"><\/span>Using SVG\u00a0<code>&lt;circle><\/code>\u00a0element<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ HTML\n&lt;div id=\"shape\">\n &lt;svg viewBox=\"0 0 100 100\">\n  &lt;circle cx=\"50\" cy=\"50\" r=\"25\" fill=\"transparent\" stroke-width=\"50\" stroke-dashoffset=\"39.2\" stroke-dasharray=\"15.7 141.3\" stroke=\"LemonChiffon\">&lt;\/circle>\n  &lt;circle cx=\"50\" cy=\"50\" r=\"25\" fill=\"transparent\" stroke-width=\"50\" stroke-dashoffset=\"23.5\" stroke-dasharray=\"31.4 125.6\" stroke=\"LightGreen\">&lt;\/circle>\n &lt;\/svg>\n&lt;\/div><\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ CSS\n#shape svg {\n width: 300px;\n height: 300px;\n border-radius: 9999px;\n background-color: WhiteSmoke;\n}<\/code><\/pre>\n\n\n\n<p><a id=\"\" rel=\"noopener\" href=\"https:\/\/jsfiddle.net\/zon3pw5s\/\" target=\"_blank\">Preview code snippet<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Using_SVG\"><\/span>Using SVG\u00a0<code>&lt;path><\/code><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/HTML\n&lt;div id=\"shape\">\n &lt;svg viewBox=\"0 0 100 100\">\n  &lt;path d=\"M50,50 L50,0 A50,50,0,0,1,79.5,9.5 Z\" fill=\"LemonChiffon\">&lt;\/path>\n  &lt;path d=\"M50,50 L79.5,9.5 A50,50,0,0,1,94,73.5 Z\" fill=\"LightGreen\">&lt;\/path>\n &lt;\/svg>\n&lt;\/div><\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ CSS\n#shape svg {\n width: 300px;\n height: 300px;\n border-radius: 9999px;\n background-color: WhiteSmoke;\n}<\/code><\/pre>\n\n\n\n<p><a id=\"\" href=\"https:\/\/jsfiddle.net\/9r7psgmw\/\" target=\"_blank\" rel=\"noopener\">Preview code snippet<\/a><\/p>\n\n\n\n<p>Each approach has its pros and cons, and offers different types of versatility:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>conic-gradient<\/code>&nbsp;approach allows you to not deal with SVG at all. Very little maths is needed but at the cost of slightly&nbsp;<a href=\"https:\/\/caniuse.com\/css-conic-gradients\" target=\"_blank\" rel=\"noreferrer noopener\">less browser compatibility<\/a>&nbsp;compared to the other solutions.<\/li>\n\n\n\n<li>The&nbsp;<code>&lt;circle&gt;<\/code>&nbsp;element solution requires you to understand how all of its parameters work. But once you\u2019ve 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.<\/li>\n\n\n\n<li>The\u00a0<code>&lt;path><\/code>\u00a0element solution requires you to understand how to calculate paths, so you need to spend some time learning the basics of SVG. But once you\u2019re comfortable with plotting, you aren\u2019t 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.<\/li>\n<\/ul>\n\n\n\n<p>The SVG&nbsp;<code>&lt;circle&gt;<\/code>&nbsp;element solution came out on top for me and for our use case: we currently don\u2019t need chart animations\/interactions and having the same implementation for both pie charts and donut charts is pretty useful.<\/p>\n\n\n\n<p>The important thing to know about the\u00a0<code>&lt;circle><\/code>\u00a0solution 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.<\/p>\n\n\n\n<p>Here\u2019s a quick overview of how the&nbsp;<code>&lt;circle&gt;<\/code>&nbsp;example above works.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Start by defining the&nbsp;<code>viewBox<\/code>&nbsp;to be&nbsp;<code>0 0 100 100<\/code>&nbsp;to simplify the calculation, fits a circle of 50 units radius. You can use&nbsp;<code>width<\/code>&nbsp;and&nbsp;<code>height<\/code>&nbsp;to define the size of the pie chart. The&nbsp;<code>viewBox<\/code>&nbsp;is the canvas we\u2019re going to be drawing on.<\/li>\n\n\n\n<li>For the circles you want to draw, you set the radius value to the half of the outcome circle\u2019s radius (that is&nbsp;<code>r=\"25\"<\/code>&nbsp;instead of 50) and set the&nbsp;<code>stroke-width<\/code>&nbsp;to be half the length of the&nbsp;<code>viewBox<\/code>&nbsp;(that is&nbsp;<code>stroke-width=\"50\"<\/code>). By doing this, you will be drawing border of the circle with thickness equals to the length of the outcome circle\u2019s radius, thus creating a solid circle.<\/li>\n\n\n\n<li>Use&nbsp;<code>stroke-dasharray<\/code>&nbsp;to set the dash length and gap size between strokes.<\/li>\n\n\n\n<li>The dash length will be the percentage of the circumference. For example, to draw 10% pie, it will be&nbsp;<code>0.1 * (2 * PI * 25) = 15.7<\/code><\/li>\n\n\n\n<li>To ensure the border dash appears only once set the gap size to be the circumference minus the dash length (that is&nbsp;<code>157 - 15.7 = 141.3<\/code>).<\/li>\n\n\n\n<li>To draw the first slice of 10% use the percentage of the circumference and the gap size we just calculated with the expression&nbsp;<code>stroke-dasharray=\"15.7 141.3\"<\/code><\/li>\n\n\n\n<li>Rotate the starting point from the default&nbsp;<code>(100, 50)<\/code>&nbsp;by 90 degrees or 25% of the circumference&nbsp;<code>stroke-dashoffset=\"39.2\"<\/code>.<\/li>\n\n\n\n<li>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&nbsp;<code>stroke-dashoffset<\/code>&nbsp;will be&nbsp;<code>(0.25 - 0.1) * CIRCUMFERENCE = 23.5<\/code>.<\/li>\n\n\n\n<li>To fill up any of the remaining area left, apply&nbsp;<code>border-radius<\/code>&nbsp;and&nbsp;<code>background-color<\/code>&nbsp;on&nbsp;<code>&lt;svg&gt;<\/code>&nbsp;element.<\/li>\n<\/ul>\n\n\n\n<p>We can implement this as a React component to make it easier for other contributors to reuse:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const CIRCLE_RADIUS = 50 \/ 2;\nconst STROKE_WIDTH = CIRCLE_RADIUS * 2;\nconst CIRCUMFERENCE = 2 * Math.PI * CIRCLE_RADIUS;\nconst DASH_OFFSET = CIRCUMFERENCE \/ 4;\n \nconst PieChart = ({\n backgroundColor = 'WhiteSmoke',\n strokeColorOptions = &#91;'LemonChiffon', 'LightGreen'],\n values = &#91;],\n}) => {\n const outputStrokeColors = _.times(values.length).map(\n   (i) => strokeColorOptions&#91;i % strokeColorOptions.length]\n );\n \n return (\n   &lt;svg\n    backgroundColor={backgroundColor}\n    viewBox=\"0 0 100 100\"\n    width=\"300px\"\n    height=\"300px\"\n    borderRadius=\"9999px\"\n   >\n    {values.map((val, index) => {\n     const strokeColor =\n      outputStrokeColors&#91;index % outputStrokeColors.length];\n     const dashSize = (val \/ 100) * CIRCUMFERENCE;\n     const gapSize = CIRCUMFERENCE - dashSize;\n     \/\/ NOTE: if it's a full circle, then no need to provide a stroke dasharray\n     const strokeDasharray =\n      val === 100 ? 'none' : `${dashSize} ${gapSize}`;\n     const accumulatedPriorPercentage = _.sum(values.slice(0, index));\n     const relativeOffset =\n       (accumulatedPriorPercentage \/ 100) * CIRCUMFERENCE;\n     const adjustedOffset = DASH_OFFSET - relativeOffset;\n \n     return (\n     &lt;circle\n       key={index}\n       cx={50}\n       cy={50}\n       fill=\"transparent\"\n       r={CIRCLE_RADIUS}\n       stroke={strokeColor}\n       strokeDasharray={strokeDasharray}\n       strokeDashoffset={adjustedOffset}\n       strokeWidth={STROKE_WIDTH}\n      \/>\n     );\n    })}\n   &lt;\/svg>\n );\n};<\/code><\/pre>\n\n\n\n<p>There might be a point where requirements change and we\u2019ll need to adopt another solution or revisit existing libraries. Until then, the lightweight and in-scope versatility of the SVG\u00a0<code>&lt;circle><\/code>\u00a0solution will serve us well.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, we share three different approaches to drawing pie charts using only HTML and CSS.<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","inline_featured_image":false,"footnotes":""},"categories":[6],"tags":[],"class_list":["post-350","post","type-post","status-publish","format-standard","hentry","category-engineering"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/posts\/350","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/comments?post=350"}],"version-history":[{"count":9,"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/posts\/350\/revisions"}],"predecessor-version":[{"id":2476,"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/posts\/350\/revisions\/2476"}],"wp:attachment":[{"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/media?parent=350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/categories?post=350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rainforestqa.com\/blog\/wp-json\/wp\/v2\/tags?post=350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}