Создание статической круговой диаграммы с помощью CSS
Я пытаюсь создать круговую диаграмму, используя CSS и HTML. Я бы просто отображал несколько статических чисел, поэтому я стараюсь сделать его относительно простым и не использовать анимацию.
В настоящее время я сталкиваюсь с препятствием на пути к созданию желаемого образа. Приведенный ниже фрагмент кода работает именно так, как я хотел бы, проблема в том, что conic-gradient не поддерживается в firefox и Internet Explorer, что будет проблемой в этом проекте.
.progress-circle {
--d: 50px;
--color: #002F65;
--progress: 40;
border-radius: var(--d);
height: var(--d);
width: var(--d);
background: conic-gradient( var(--color) calc(calc(var(--progress) / 100) * 360deg), transparent calc(calc(var(--progress) / 100) * 360deg));
}
<div class="progress-circle"></div>
Я искал альтернативу, похожую на приведенный выше пример, который привел меня к этой статье: designing simple pie charts with css
Моя проблема в том, что способ расчета процентного роста круговой диаграммы, похоже, несовместим с тем, что я пытаюсь достичь. как это определено преобразованием: повернуть (.1turn);
Мой главный вопрос, можно ли сделать конический градиент совместимым с другими браузерами? Если нет, то как лучше всего подойти к созданию круговой диаграммы с помощью CSS, чтобы она напоминала первый пример?
Для контекста я буду передавать данные из массива, чтобы определить процентную долю круговой диаграммы.
.pie {
width: 100px; height: 100px;
border-radius: 50%;
background: yellowgreen;
background-image:
linear-gradient(to right, transparent 50%, #655 0);
}
.pie::before {
content: "";
display: block;
margin-left: 50%;
height: 100%;
border-radius: 0 100% 100% 0 / 50%;
background: #655;
transform-origin: left;
transform: rotate(.1turn);
}
<div class="pie"></div>
Свободный перевод вопроса Creating a static pie chart with CSS от участника @stepheniok.
Ответы (2 шт):
Вот идея, основанная на этом предыдущем ответе
.box {
/* percentage to degree
--s:0 for [0% 50%]
--s:1 for [50% 100%]
*/
--v:calc( ((18/5) * var(--p) - 90)*1deg);
width:100px;
height:100px;
display:inline-block;
border-radius:50%;
background:
linear-gradient(var(--v), yellowgreen 50%,transparent 0) 0 /calc((1 - var(--s))*100%),
linear-gradient(var(--v), transparent 50%,#655 0) 0 /calc(var(--s)*100%),
linear-gradient(to right, yellowgreen 50%,#655 0);
}
<div class="box" style="--p:5;--s:0"></div>
<div class="box" style="--p:20;--s:0"></div>
<div class="box" style="--p:50;--s:0"></div>
<div class="box" style="--p:70;--s:1"></div>
<div class="box" style="--p:95;--s:1"></div>
Мы можем оптимизировать код с помощью min() и сохранить использование только одной переменной, но вам нужно обратить внимание на поддержку: https://caniuse.com/#feat=css-math-functions
.box {
/* percentage to degree */
--v:calc( ((18/5) * var(--p) - 90)*1deg);
width:100px;
height:100px;
display:inline-block;
border-radius:50%;
background:
linear-gradient(var(--v), yellowgreen 50%,transparent 0) 0 /min(100%,(50 - var(--p))*100%),
linear-gradient(var(--v), transparent 50%,#655 0) 0 /min(100%,(var(--p) - 50)*100%),
linear-gradient(to right, yellowgreen 50%,#655 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:70;"></div>
<div class="box" style="--p:95;"></div>
Еще одна идея с использованием псевдоэлемента с большей поддержкой:
.box {
/* percentage to degree */
--v: calc( ((18/5) * var(--p) - 180)*1deg);
width: 100px;
display: inline-flex;
border-radius: 50%;
overflow: hidden;
background: linear-gradient(to right, yellowgreen 50%, #655 0);
}
.box::before,
.box::after {
content: "";
width: 50%;
padding-top:100%;
transform: rotate(var(--v));
}
.box::before {
background:
linear-gradient(yellowgreen 0 0)
0 / calc((50 - var(--p))*1%);
transform-origin: right;
}
.box::after {
background:
linear-gradient(#655 0 0)
0 / calc((var(--p) - 50)*1%);
transform-origin: left;
}
<div class="box" style="--p:5;"></div>
<div class="box" style="--p:20;width:150px;"></div>
<div class="box" style="--p:50;width:120px;"></div>
<div class="box" style="--p:70;"></div>
<div class="box" style="--p:95;width:80px;"></div>
Свободный перевод ответа от участника @Temani Afif.
CSS решение
Основано на изменении параметров атрибута stroke-dasharray
Чтобы установить нужные значения атрибута в соответствии с углом открытия сектора, нужно узнать полную длину окружности при выбранном радиусе.
Допустим радиус равен 50px
let radius = 50;
let circumference = radius * 2 * Math.PI;
console.log(circumference );
Полная длина окружности circumference ~=314px
Например, чтобы нарисовать сегмент равный четверти круга:
вычисляем длину черты: 314 * 0.25 = 78.5px
Длина пробела : 314 * 0.75 = 235.5px
Итого для значения stroke-dasharray="78.5, 235.5"
Аналогичным образом установлены параметры stroke-dasharray для другиx углов секторов
circle {
fill:#665555;
}
#p15,#p90,#p180,#p270 {
fill:none;
stroke:#9ACD32;
stroke-width:100;
}
#p15 {
stroke-dasharray:15.7,298.3;
}
#p90 {
stroke-dasharray:78.5,235.5;
}
#p180 {
stroke-dasharray:157,157;
}
#p270 {
stroke-dasharray:235.5,78.5;
}
<svg width="200" height="200" style="border:1px solid">
<circle id="bg" r="100" cx="100" cy="100" />
<path id="p15" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50">
</path>
</svg>
<!-- 90deg -->
<svg width="200" height="200" style="border:1px solid">
<circle r="100" cx="100" cy="100" />
<path id="p90" stroke-dasharray="78.5,235.5" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50">
</path>
</svg>
<!-- 180deg -->
<svg width="200" height="200" style="border:1px solid">
<circle r="100" cx="100" cy="100" />
<path id="p180" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50">
</path>
</svg>
<!-- 270deg -->
<svg width="200" height="200" style="border:1px solid">
<circle r="100" cx="100" cy="100" />
<path id="p270" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50">
</path>
</svg>
<script>
let radius = 50;
let circumference = radius * 2 * Math.PI;
console.log(circumference );
</script>
SVG решение
Как и в примере решения CSS используется изменение атрибутов stroke-dasharray
<svg width="200" height="200">
<circle id="bg" r="100" cx="100" cy="100" fill="#665555"/>
<path stroke-dasharray="300 14" stroke-dashoffset="300" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50" id="p1"
r="50" cx="100" cy="100" stroke="#9ACD32" stroke-width="100" fill="none" >
</path>
</svg>
<svg width="200" height="200">
<circle r="100" cx="100" cy="100" fill="#665555"/>
<path stroke-dasharray="235.5 78.5" stroke-dashoffset="235.5" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50" stroke="#9ACD32" stroke-width="100" fill="none" >
</path>
</svg>
<svg width="200" height="200">
<circle id="bg" r="100" cx="100" cy="100" fill="#665555"/>
<path stroke-dasharray="157 157" stroke-dashoffset="157" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50" stroke="#9ACD32" stroke-width="100" fill="none" >
</path>
</svg>
<svg width="200" height="200">
<circle id="bg" r="100" cx="100" cy="100" fill="#665555"/>
<path stroke-dasharray="78.5 235.5" stroke-dashoffset="78.5" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50" stroke="#9ACD32" stroke-width="100" fill="none" >
</path>
</svg>
Пример интерактивного изменения угла сектора
Для контекста я буду передавать данные из массива, чтобы определить процентную долю круговой диаграммы.
С помощью input и JS смоделирован этот процесс:
- Вывод процентов заполнения диаграммы
let circumference = 50 * 2 * Math.PI,
input = document.querySelector("[type='range']"),
txt = document.querySelector("#txt1");
input.addEventListener("input",()=>{
pieChart();
})
window.addEventListener("load",()=>{
pieChart();
})
function pieChart(){
let val = Number(input.value);
let dash = circumference * val / 100;
let gap = circumference - dash;
p15.style.strokeDasharray = dash + " " + gap
txt.innerHTML = (val + '%');
}
<svg width="200" height="200" >
<circle id="bg" r="100" cx="100" cy="100" fill="#665555"/>
<path id="p15" stroke-dasharray="15.7,298.3" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50" id="p1" fill="none" stroke="#9ACD32" stroke-width="100" >
</path>
<text id="txt1" y="60%" x="50%" text-anchor="middle" font-size="32px" fill="white">0</text>
</svg>
<div><input id="size" step="1" type="range" min="0" max = "100" value="0" /></div>
- Вывод угла заполнения диаграммы
let circumference = 50 * 2 * Math.PI,
input = document.querySelector("[type='range']"),
txt = document.querySelector("#txt1");
input.addEventListener("input",()=>{
pieChart();
})
window.addEventListener("load",()=>{
pieChart();
})
function pieChart(){
let val = Number(input.value);
let dash = circumference * val / 360;
let gap = circumference - dash;
p15.style.strokeDasharray = dash + " " + gap
txt.innerHTML = (val);
}
<svg width="200" height="200" >
<circle id="bg" r="100" cx="100" cy="100" fill="#665555"/>
<path id="p15" stroke-dasharray="15.7,298.3" d="M100,50A50,50 0 0 1 100,150A50,50 0 0 1 100,50" id="p1" fill="none" stroke="#9ACD32" stroke-width="100" >
</path>
<text id="txt1" y="60%" x="50%" text-anchor="middle" font-size="32px" fill="white">0</text>
</svg>
<div><input id="size" step="1" type="range" min="0" max = "360" value="0" /></div>
Свободный перевод ответа от участника @Alexandr_TT.