Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Percent pie chart with css only

I've found pretty nice "percent pie chart" and want to create it with CSS only. No animation is required. Just static "picture".

Example 1

I understand If I wanna create this kind of chart I need to use elements like these

Example 2

The questions are

  1. How to create element #2 ?
  2. How to manage shape of element #2 for smaller (5%) or higher percent (80%) values ?
like image 657
Vic VKh Avatar asked Nov 27 '22 10:11

Vic VKh


1 Answers

New answer 2021

With some modern techniques we can improve the code. You can have rounded edges and also consider animation:

@property --p{
  syntax: '<number>';
  inherits: true;
  initial-value: 1;
}

.pie {
  --p:20;      /* the percentage */
  --b:22px;    /* the thickness */
  --c:darkred; /* the color */
  --w:150px;   /* the size*/
  
  width:var(--w);
  aspect-ratio:1/1;
  position:relative;
  display:inline-grid;
  margin:5px;
  place-content:center;
  font-size:25px;
  font-weight:bold;
  font-family:sans-serif;
}
.pie:before,
.pie:after {
  content:"";
  position:absolute;
  border-radius:50%;
}
.pie:before {
  inset:0;
  background:
    radial-gradient(farthest-side,var(--c) 98%,#0000) top/var(--b) var(--b) no-repeat,
    conic-gradient(var(--c) calc(var(--p)*1%),#0000 0);
  -webkit-mask:radial-gradient(farthest-side,#0000 calc(99% - var(--b)),#000 calc(100% - var(--b)));
          mask:radial-gradient(farthest-side,#0000 calc(99% - var(--b)),#000 calc(100% - var(--b)));
}
.pie:after {
  inset:calc(50% - var(--b)/2);
  background:var(--c);
  transform:rotate(calc(var(--p)*3.6deg - 90deg)) translate(calc(var(--w)/2 - 50%));
}
.animate {
  animation:p 1s .5s both;
}
.no-round:before {
  background-size:0 0,auto;
}
.no-round:after {
  content:none;
}
@keyframes p{
  from{--p:0;}
}

body {
  background:#ddd;
}
<div class="pie" style="--p:20"> 20%</div>
<div class="pie" style="--p:40;--c:darkblue;--b:10px"> 40%</div>
<div class="pie no-round" style="--p:60;--c:purple;--b:15px"> 60%</div>
<div class="pie animate" style="--p:80;--c:orange;"> 80%</div>
<div class="pie animate no-round" style="--p:90;--c:lightgreen"> 90%</div>

Old answer

You can do this with multiple background.

From 0% to 50%:

.box {
  width: 100px;
  height: 100px;
  display: inline-block;
  border-radius: 50%;
  padding: 5px;
  background: 
    linear-gradient(#ccc, #ccc) content-box, 
    linear-gradient(var(--v), #f2f2f2 50%, transparent 0),
    linear-gradient(to right, #f2f2f2 50%, blue 0);
}
<div class="box" style="--v:-90deg"></div><!-- 0% -->
<div class="box" style="--v:-45deg"></div><!-- 12.5% -->
<div class="box" style="--v:  0deg"></div><!-- 25% -->
<div class="box" style="--v: 45deg"></div><!-- 37.5% -->
<div class="box" style="--v: 90deg"></div><!-- 50% -->

<p>The formula is [p = (18/5) * x - 90]. <small>Where x is the percentage and p the degree</small></p>
<p>for x = 5% --> p = -72deg </p>
<div class="box" style="--v:-72deg"></div>

From 50% to 100%:

.box {
  width:100px;
  height:100px;
  display:inline-block;
  border-radius:50%;
  padding:5px;
  background:
    linear-gradient(#ccc,#ccc) content-box,
    linear-gradient(var(--v), blue 50%,transparent 0),
    linear-gradient(to right, #f2f2f2 50%,blue 0);
}
<div class="box" style="--v:-90deg"></div><!-- 50% -->
<div class="box" style="--v:-45deg"></div><!-- 62.5% -->
<div class="box" style="--v:  0deg"></div><!-- 75% -->
<div class="box" style="--v: 45deg"></div><!-- 87.5% -->
<div class="box" style="--v: 90deg"></div><!-- 100% -->

<p>The formula is [p = (18/5) * x - 270]. <small>Where x is the percentage and p the degree</small></p>
<p>for x = 80% --> p = 18deg </p>
<div class="box" style="--v:18deg"></div>

You can combine both like this:

.box {
  width:100px;
  height:100px;
  display:inline-block;
  border-radius:50%;
  padding:5px;
  background:
    linear-gradient(#ccc,#ccc) content-box,
    linear-gradient(var(--v), #f2f2f2 50%,transparent 0) 0/calc(var(--s)*100%)      ,
    linear-gradient(var(--v), blue    50%,transparent 0) 0/calc((1 - var(--s))*100%),
    linear-gradient(to right, #f2f2f2 50%,blue 0);
}
<div class="box" style="--v:-90deg;--s:1"></div>
<div class="box" style="--v:0deg;--s:1"></div>
<div class="box" style="--v:90deg;--s:1"></div>
<div class="box" style="--v:0deg;--s:0"></div>
<div class="box" style="--v:90deg;--s:0"></div>

Now we can optimize like below to consider percetange value:

.box {
  
  --v:calc( ((18/5) * var(--p) - 90)*1deg);

  width:100px;
  height:100px;
  display:inline-block;
  border-radius:50%;
  padding:10px;
  background:
    linear-gradient(#ccc,#ccc) content-box,
    linear-gradient(var(--v), #f2f2f2     50%,transparent 0) 0/min(100%,(50 - var(--p))*100%),
    linear-gradient(var(--v), transparent 50%,blue        0) 0/min(100%,(var(--p) - 50)*100%),
    linear-gradient(to right, #f2f2f2 50%,blue 0);
}
<div class="box" style="--p:5;"></div>
<div class="box" style="--p:20;"></div>
<div class="box" style="--p:50;"></div>
<div class="box" style="--p:60;"></div>
<div class="box" style="--p:75;"></div>
<div class="box" style="--p:100;"></div>

CSS pie chart

Related question to get another version: Creating a static pie chart with CSS


We can also consider mask to add transparency:

.box {
  
  --v:calc( ((18/5) * var(--p) - 90)*1deg);

  width:100px;
  height:100px;
  display:inline-block;
  border-radius:50%;
  padding:10px;
  background:
    linear-gradient(var(--v), #f2f2f2     50%,transparent 0) 0/min(100%,(50 - var(--p))*100%),
    linear-gradient(var(--v), transparent 50%,blue        0) 0/min(100%,(var(--p) - 50)*100%),
    linear-gradient(to right, #f2f2f2 50%,blue 0);
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite:destination-out;
  mask-composite:exclude;
}

body {
  background:linear-gradient(to right,red,yellow);
}
<div class="box" style="--p:5;"></div>
<div class="box" style="--p:20;"></div>
<div class="box" style="--p:50;"></div>
<div class="box" style="--p:60;"></div>
<div class="box" style="--p:75;"></div>
<div class="box" style="--p:100;"></div>

CSS pie chart with transparency

Also like below:

.box {
  
  --v:calc( ((18/5) * var(--p) - 90)*1deg);

  width:100px;
  height:100px;
  display:inline-block;
  border-radius:50%;
  padding:10px;
  background:
    linear-gradient(var(--v), transparent 50%,blue        0) 0/min(100%,(var(--p) - 50)*100%),
    linear-gradient(to right, transparent 50%,blue 0);
  -webkit-mask:
    linear-gradient(var(--v), #f2f2f2     50%,transparent 0) 0/min(100%,(50 - var(--p))*100%),
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite:destination-out;
  mask-composite:exclude;
}

body {
  background:linear-gradient(to right,red,yellow);
}
<div class="box" style="--p:5;"></div>
<div class="box" style="--p:20;"></div>
<div class="box" style="--p:50;"></div>
<div class="box" style="--p:60;"></div>
<div class="box" style="--p:75;"></div>
<div class="box" style="--p:100;"></div>

transparent pie chart using CSS mask

Related: Border Gradient with Border Radius

like image 199
Temani Afif Avatar answered Dec 04 '22 13:12

Temani Afif