CSS Only Pie Chart - Как добавить интервал / отступ между сегментами?

Я построил круговую диаграмму только для CSS, но мне нужно добавить пространство между каждым сегментом, как если бы это была граница. Я попытался добавить границу к каждому фрагменту, но на самом деле это не сработало.

Есть идеи, как сделать эту круговую диаграмму CSS более похожей на следующее изображение?

введите сюда описание изображения

Мой код:

.palette {
  height: 48px;
  width: 48px;
}
.palette .colorOuter1,
.palette .colorOuter2,
.palette .colorOuter3,
.palette .colorOuter4,
.palette .colorOuter5 {
  height: 200px;
  width: 200px;
  border-radius: 50%;
  position: absolute;
  top: 0;
  left: 0;
  clip: rect(0px, 200px, 200px, 100px);
}
.palette .colorOuter1 {
  clip: auto;
  background: #eee;
}
.palette .colorOuter2 {
  transform: rotate(0deg);
}
.palette .colorOuter2 {
  transform: rotate(72deg);
}
.palette .colorOuter3 {
  transform: rotate(144deg);
}
.palette .colorOuter4 {
  transform: rotate(216deg);
}
.palette .colorOuter5 {
  transform: rotate(288deg);
}
.palette .colorInner1,
.palette .colorInner2,
.palette .colorInner3,
.palette .colorInner4,
.palette .colorInner5 {
  position: absolute;
  top: 0;
  left: 0;
  width: 200px;
  height: 200px;
  border-radius: 50%;
  transform: rotate(72deg);
}
.palette .colorInner1 {
  background: #5D5E63;
  clip: rect(0px, 100px, 200px, 0px);
}
.palette .colorInner2 {
  background-color: #AEADA9;
  clip: rect(0px, 100px, 100px, 0px);
}
.palette .colorInner3 {
  background-color: #D5C4A8;
  clip: rect(0px, 100px, 100px, 0px);
}
.palette .colorInner4 {
  background-color: #AA875F;
  clip: rect(0px, 100px, 100px, 0px);
}
.palette .colorInner5 {
  background-color: #B7CBC7;
  clip: rect(0px, 100px, 100px, 0px);
}
.palette .centerOverlay {
  position: absolute;
  border-radius: 50%;
  top: 60px;
  left: 60px;
  background: #fff;
  width: 80px;
  height: 80px;
  display: block;
  clip: auto;
}
<div class="palette">
  <div class="colorOuter1">
    <div class="colorInner1"></div>
  </div>
  <div class="colorOuter2">
    <div class="colorInner2"></div>
  </div>
  <div class="colorOuter3">
    <div class="colorInner3"></div>
  </div>
  <div class="colorOuter4">
    <div class="colorInner4"></div>
  </div>
  <div class="colorOuter5">
    <div class="colorInner5"></div>
  </div>
  <div class="centerOverlay"></div>
</div>

Свободный перевод вопроса CSS Only Pie Chart - How to add spacing/padding between slices? от участника @stwhite.


Ответы (1 шт):

Автор решения: Alexandr_TT

Сначала я бы воссоздал это с меньшим количеством кода, с помощью clip-path, как показано ниже:

.palette {
  height: 200px;
  width: 200px;
  position:relative;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:50px solid var(--c,red);
  border-radius:50%;
  clip-path:polygon(50% 50%, 50% 0%, 100% 0%,100% 33.745%); 
}
.color1 {
  transform:rotate(72deg);
  --c:blue;
}
.color2 {
  transform:rotate(144deg);
  --c:orange;
}
.color3 {
  transform:rotate(-72deg);
  --c:green;
}
.color4 {
  transform:rotate(-144deg);
  --c:purple;
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
</div>

Затем вы можете применить translation к своим элементам, чтобы создать пробелы:

.palette {
  height: 200px;
  width: 200px;
  position:relative;
  margin:20px;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:50px solid var(--c,red);
  border-radius:50%;
  clip-path:polygon(50% 50%, 50% 0%, 100% 0%,100% 33.745%); 
}
.color1 {
  transform:rotate(72deg) translate(5px,-5px);
  --c:blue;
}
.color2 {
  transform:rotate(144deg) translate(5px,-5px);
  --c:orange;
}
.color3 {
  transform:rotate(-72deg) translate(5px,-5px);
  --c:green;
}
.color4 {
  transform:rotate(-144deg) translate(5px,-5px);
  --c:purple;
}
.color5 {
  transform:rotate(0) translate(5px,-5px);
  --c:red;
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
</div>

Или, если вы хотите создать эффект отсутствующих частей, чтобы сохранить круглую форму (как показано на скриншоте), вам необходимо настроить траекторию clip-path.

.palette {
  height: 200px;
  width: 200px;
  position:relative;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:50px solid var(--c,red);
  border-radius:50%;
  clip-path:polygon(
    calc(50% + 5px) 50%, 
    calc(50% + 5px) 0%, 
    100% 0%,
    100% calc(33.745% - 5px),
    50% calc(50% - 5px)); 
}
.color1 {
  transform:rotate(72deg);
  --c:blue;
}
.color2 {
  transform:rotate(144deg);
  --c:orange;
}
.color3 {
  transform:rotate(-72deg);
  --c:green;
}
.color4 {
  transform:rotate(-144deg);
  --c:purple;
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
</div>

А с переменными CSS мы легко справимся со всем:

.palette {
  --g:10px; /* The gap between shapes*/
  --s:50px; /* the size*/

  height: 200px;
  width: 200px;
  position:relative;
  display:inline-block;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:var(--s) solid var(--c,red);
  border-radius:50%;
  clip-path:polygon(
    calc(50% + var(--g)/2) 50%, 
    calc(50% + var(--g)/2) 0%, 
    100% 0%,
    100% calc(33.745% - var(--g)/2),
    50% calc(50% - var(--g)/2)); 
}
.color1 {
  transform:rotate(72deg);
  --c:blue;
}
.color2 {
  transform:rotate(144deg);
  --c:orange;
}
.color3 {
  transform:rotate(-72deg);
  --c:green;
}
.color4 {
  transform:rotate(-144deg);
  --c:purple;
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
</div>

<div class="palette" style="--s:40px;--g:20px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
</div>

<div class="palette" style="--s:60px;--g:0px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
</div>

введите сюда описание изображения

Используя тот же код, вы можете легко масштабировать до любого количества сегментов:

С 8 сегментами:

.palette {
  --g:10px; /* The gap between shapes*/
  --s:50px; /* the size*/

  height: 200px;
  width: 200px;
  position:relative;
  display:inline-block;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:var(--s) solid var(--c,red);
  border-radius:50%;
  clip-path:polygon(
    calc(50% + var(--g)/2) 50%, 
    calc(50% + var(--g)/2) 0%, 
    100% 0%,
    100% calc(0% - var(--g)/2),
    50% calc(50% - var(--g)/2)); 
}
.color1 {
  transform:rotate(45deg); /* 360/8 */
  --c:blue;
}
.color2 {
  transform:rotate(90deg);
  --c:orange;
}
.color3 {
  transform:rotate(135deg);
  --c:green;
}
.color4 {
  transform:rotate(180deg);
  --c:purple;
}
.color5 {
  transform:rotate(-45deg); /* 360/8 */
  --c:orange;
}
.color6 {
  transform:rotate(-90deg);
  --c:lightblue;
}
.color7 {
  transform:rotate(-135deg);
  --c:pink;
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
</div>

<div class="palette" style="--s:40px;--g:20px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
</div>

<div class="palette" style="--s:60px;--g:0px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
</div>

введите сюда описание изображения

Три сегмента:

.palette {
  --g:10px; /* The gap between shapes*/
  --s:50px; /* the size*/

  height: 200px;
  width: 200px;
  position:relative;
  display:inline-block;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:var(--s) solid var(--c,red);
  border-radius:50%;
  clip-path:polygon(
    calc(50% + var(--g)/2) 50%, 
    calc(50% + var(--g)/2) 0%, 
    100% 0%,
    100% calc(78.665% - var(--g)/2),
    50% calc(50% - var(--g)/2)); 
}
.color1 {
  transform:rotate(120deg);
  --c:blue;
}
.color2 {
  transform:rotate(-120deg);
  --c:orange;
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
</div>

<div class="palette" style="--s:40px;--g:20px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
</div>

<div class="palette" style="--s:60px;--g:0px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
</div>

введите сюда описание изображения


Вот формула магического числа, используемого в clip-path:

  • 5 сегментов: 33.75% = 50% - tan(90deg - 72deg)*50%
  • 8 сегментов: 0% = 50% - tan(90deg - 45deg)*50%
  • 3 сегмента: 78.665% = 50% - tan(90deg - 120deg)*50%

Таким образом, общая формула для N сегментов составляет 50% * (1 - tan (90deg - 360deg / N)) с N в [3 8].
Если N <3, у нас есть тривиальные случаи, когда нам не нужен сложный код.
Для N > 8 нам нужен другой путь отсечения и другая формула: 50% * (1 + tan (360deg / N))

Пример с 10 сегментами:

.palette {
  --g:10px; /* The gap between shapes*/
  --s:50px; /* the size*/

  height: 200px;
  width: 200px;
  position:relative;
  display:inline-block;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:var(--s) solid var(--c,red);
  border-radius:50%;
  clip-path:polygon(
    calc(50% + var(--g)/2) 50%, 
    calc(50% + var(--g)/2) 0%, 
    calc(86.327% - var(--g)/2) 0%,
    50% calc(50% - var(--g)/2)); 
}
.color1 {
  transform:rotate(36deg);
  --c:blue;
}
.color2 {
  transform:rotate(72deg);
  --c:orange;
}
.color3 {
  transform:rotate(108deg);
  --c:green;
}
.color4 {
  transform:rotate(144deg);
  --c:purple;
}
.color5 {
  transform:rotate(180deg);
  --c:lightblue;
}
.color6 {
  transform:rotate(-36deg);
  --c:silver;
}
.color7 {
  transform:rotate(-72deg);
  --c:black;
}
.color8 {
  transform:rotate(-108deg);
  --c:darkgreen;
}
.color9 {
  transform:rotate(-144deg);
  --c:pink;
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
  <div class="color9"></div>
  <div class="color10"></div>
</div>

<div class="palette" style="--s:40px;--g:20px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
  <div class="color9"></div>
  <div class="color10"></div>
</div>

<div class="palette" style="--s:60px;--g:0px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
  <div class="color9"></div>
  <div class="color10"></div>
</div>

введите сюда описание изображения

Идея на случай, если вы хотите добиться градиентной окраски. При этом используется конический градиент, поэтому пока не будет работать в Firefox:

.palette {
  --g:10px; /* The gap between shapes*/
  --s:50px; /* the size*/

  height: 200px;
  width: 200px;
  position:relative;
  display:inline-block;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:var(--s) solid transparent;
  background:
     linear-gradient(#fff,#fff) padding-box,
     conic-gradient(from calc(-1*var(--d,0deg)), red,blue,green) border-box;
  border-radius:50%;
  clip-path:polygon(
    calc(50% + var(--g)/2) 50%, 
    calc(50% + var(--g)/2) 0%, 
    calc(86.327% - var(--g)/2) 0%,
    50% calc(50% - var(--g)/2)); 
  transform:rotate(var(--d,0deg));
}
.color1 {
  --d:36deg;
}
.color2 {
  --d:72deg;
}
.color3 {
  --d:108deg;
}
.color4 {
  --d:144deg;
}
.color5 {
  --d:180deg;
}
.color6 {
  --d:-36deg;
}
.color7 {
  --d:-72deg;
}
.color8 {
  --d:-108deg;
}
.color9 {
  --d:-144deg;
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
  <div class="color9"></div>
  <div class="color10"></div>
</div>

<div class="palette" style="--s:40px;--g:20px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
  <div class="color9"></div>
  <div class="color10"></div>
</div>

<div class="palette" style="--s:60px;--g:0px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
  <div class="color9"></div>
  <div class="color10"></div>
</div>

введите сюда описание изображения

Вы можете смоделировать другую окраску градиента с помощью linear-gradient, и у вас будет лучшая поддержка:

.palette {
  --g:10px; /* The gap between shapes*/
  --s:50px; /* the size*/

  height: 200px;
  width: 200px;
  position:relative;
  display:inline-block;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  padding:var(--s);
  background:
     linear-gradient(#fff,#fff)      content-box,
     linear-gradient(red,blue,green) padding-box;
  border-radius:50%;
  clip-path:polygon(
    calc(50% + var(--g)/2) 50%, 
    calc(50% + var(--g)/2) 0%, 
    100% 0%,
    100% calc(0% - var(--g)/2),
    50% calc(50% - var(--g)/2)); 
  transform:rotate(var(--d,0deg));
}
.palette > *:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  padding:inherit;
  background:inherit;
  transform:rotate(calc(-1*var(--d,0deg)));
  border-radius:50%;
}
.color1 {
  --d:45deg;
}
.color2 {
  --d:90deg;
}
.color3 {
  --d:135deg;
}
.color4 {
  --d:180deg;
}
.color5 {
  --d:-45deg;
}
.color6 {
  --d:-90deg;
}
.color7 {
  --d:-135deg;
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
</div>

<div class="palette" style="--s:40px;--g:20px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
</div>

<div class="palette" style="--s:60px;--g:0px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
</div>

введите сюда описание изображения

Мы можем добавить маску для полной прозрачности:

.palette {
  --g:10px; /* The gap between shapes*/
  --s:50px; /* the size*/

  height: 200px;
  width: 200px;
  position:relative;
  display:inline-block;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  background:linear-gradient(red,blue,green) padding-box;
  -webkit-mask:radial-gradient(farthest-side,transparent calc(99% - var(--s)),#fff calc(100% - var(--s))); 
  mask:radial-gradient(farthest-side,transparent calc(99% - var(--s)),#fff calc(100% - var(--s))); 
  border-radius:50%;
  clip-path:polygon(
    calc(50% + var(--g)/2) 50%, 
    calc(50% + var(--g)/2) 0%, 
    100% 0%,
    100% calc(0% - var(--g)/2),
    50% calc(50% - var(--g)/2)); 
  transform:rotate(var(--d,0deg));
}
.palette > *:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  background:inherit;
  transform:rotate(calc(-1*var(--d,0deg)));
  border-radius:50%;
}
.color1 {
  --d:45deg;
}
.color2 {
  --d:90deg;
}
.color3 {
  --d:135deg;
}
.color4 {
  --d:180deg;
}
.color5 {
  --d:-45deg;
}
.color6 {
  --d:-90deg;
}
.color7 {
  --d:-135deg;
}

body {
  background:linear-gradient(to left,grey,#fff);
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
</div>

<div class="palette" style="--s:40px;--g:20px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
</div>

<div class="palette" style="--s:60px;--g:0px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
</div>

То же самое и с коническим градиентом:

.palette {
  --g:10px; /* The gap between shapes*/
  --s:50px; /* the size*/

  height: 200px;
  width: 200px;
  position:relative;
  display:inline-block;
  overflow:hidden;
}
.palette > * {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  background: conic-gradient(from calc(-1*var(--d,0deg)), red,blue,green);
  -webkit-mask:radial-gradient(farthest-side,transparent calc(99% - var(--s)),#fff calc(100% - var(--s))); 
  mask:radial-gradient(farthest-side,transparent calc(99% - var(--s)),#fff calc(100% - var(--s))); 
  
  border-radius:50%;
  clip-path:polygon(
    calc(50% + var(--g)/2) 50%, 
    calc(50% + var(--g)/2) 0%, 
    calc(86.327% - var(--g)/2) 0%,
    50% calc(50% - var(--g)/2)); 
  transform:rotate(var(--d,0deg));
}
.color1 {
  --d:36deg;
}
.color2 {
  --d:72deg;
}
.color3 {
  --d:108deg;
}
.color4 {
  --d:144deg;
}
.color5 {
  --d:180deg;
}
.color6 {
  --d:-36deg;
}
.color7 {
  --d:-72deg;
}
.color8 {
  --d:-108deg;
}
.color9 {
  --d:-144deg;
}

body {
  background:linear-gradient(to left,grey,white);
}
<div class="palette">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
  <div class="color9"></div>
  <div class="color10"></div>
</div>

<div class="palette" style="--s:40px;--g:20px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
  <div class="color9"></div>
  <div class="color10"></div>
</div>

<div class="palette" style="--s:60px;--g:0px">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
  <div class="color7"></div>
  <div class="color8"></div>
  <div class="color9"></div>
  <div class="color10"></div>
</div>

Свободный перевод ответа от участника @Temani Afif.

→ Ссылка