如何使用纯CSS将HTML5进度元素样式化为圆形/饼状图?

27

HTML5引入了一个新的“进度”元素,它默认呈现为一个进度条(温度计)。

一个非常基本的例子是:

<progress max="100" value="85"></progress>

我一直在使用JavaScript尝试各种进度圆选项,并对这里讨论的一些纯CSS方法印象深刻:CSS Progress Circle

我想知道是否有人成功地将CSS应用于“progress”元素以提供饼图/时钟/圆形渲染而不是线性显示?

编辑/附加说明: “meter”元素与“progress”非常相似,但提供了低/高范围...我之所以提到这一点,更多是为那些将来可能会偶然发现这篇文章并想将类似技术应用于HTML5 meter元素的人提供帮助。


1
哎呀,我多么希望我们有锥形渐变……那会让这个变得非常容易。 - Facundo Corradini
5个回答

16

尝试仅使用CSS实现这个功能相当困难,因此我认为这不是正确的技术方法。

无论如何,仅作为技术练习,让我们尝试一下。(仅在Chrome浏览器中测试过!)

首先是基础。我们将把圆形分为4个象限,并且每个象限都需要不同的样式。这里展示了样式,用颜色(绿色、红色、蓝色、黄色)显示了进度值元素的有用范围。灰色区域是元素的其余未使用部分。

.test {
  width: 100px;
  height: 100px;
  margin: 20px 10px 0px 20px;
  border-radius: 50%;
  background-image: radial-gradient(lightblue 62%, blue 40%);
  position: relative;
  display: inline-block;
}

.test div {
 height: 30%;
 transform-origin: left top;
    position: absolute;
    opacity: 0.5;
 ackground-color: green;
}

.inner1 {
 width: 25%;
 left: 50%;
    top: -20%;
 background-color: green;
 transform: rotate(45deg) scaleX(3.9598);
}

.inner2 {
 width: 50%;
 left: 190%;
    top: -20%;
 background-image: linear-gradient(to right,gray 50%, red 50%);
 transform: rotate(135deg) scaleX(3.9598);
}

.inner3 {
 width: 75%;
 left: 190%;
    top: 260%;
 background-image: linear-gradient(to right,gray 66%, blue 66%);
 transform: rotate(225deg) scaleX(3.9598);
}

.inner4 {
 width: 100%;
 left: -230%;
    top: 260%;
 background-image: linear-gradient(to right,gray 75%, yellow 66%);
 transform: rotate(315deg) scaleX(3.9598);
}
<div class="test">
    <div class="inner1"></div>
</div>
<div class="test">
    <div class="inner2"></div>
</div>
<div class="test">
    <div class="inner3"></div>
</div>
<div class="test">
    <div class="inner4"></div>
</div>

现在,让我们展示一个创建径向线段的技巧。可以通过将元素设置为垂直于用户并应用一些透视来实现:

div {
 width: 300px;
 height: 300px;
 position: relative;
}

.container {
 perspective: 400px;
 margin: 40px 200px;
 border: solid 1px black;
}

.top {
    position: absolute;
    left: 0px;
    top: -100%;
    background-image: repeating-linear-gradient(to right, tomato 0px, white 20px);
    transform: rotateX(90deg);
    transform-origin: center bottom; 
}

.right {
    position: absolute;
    left: 100%;
    top: 0px;
    background-image: repeating-linear-gradient( tomato 0px, white 20px);
    transform: rotateY(90deg);
    transform-origin: left center; 
}

.bottom {
    position: absolute;
    left: 0px;
    bottom: 0px;
    background-image: repeating-linear-gradient(to right, tomato 0px, white 20px);
    transform: rotateX(90deg);
    transform-origin: center bottom; 
}

.left {
    position: absolute;
    right: 100%;
    top: 0px;
    background-image: repeating-linear-gradient( tomato 0px, white 20px);
    transform: rotateY(-90deg);
    transform-origin: right center; 
}
<div class="container">
<div class="top"></div>
<div class="right"></div>
<div class="bottom"></div>
<div class="left"></div>
</div>

现在,只需要一些乏味的选择器(要想针对值在20-29范围内进行定位而不同时针对值2进行定位是很困难的)。

有一点JS代码,但只用于控制进度值。您可以使用输入框和滑块来更改它。

function change () {
    var input = document.getElementById("input");
    var progress = document.getElementById("test");
    progress.value = input.value;
}

function changeNumber () {
    var input = document.getElementById("number");
    var progress = document.getElementById("test");
    progress.value = input.value;
}
.container {
 width: 500px;
 height: 500px;
 overflow: hidden;
 margin: 10px;
}
.test {
  width: 200px;
  height: 200px;
  margin: 10px 10px;
  border-radius: 50%;
  background-image: radial-gradient(lightblue 62%, transparent 40%);
  box-shadow: 0px 0px 0px 500px lightblue, inset 0px 0px 0px 2px lightblue;
}



.test::-webkit-progress-bar {
 background-color: transparent;
 position: relative;
    border-radius: 50%;
    perspective: 100px;
    z-index: -1;
 background-repeat: no-repeat;
}

.test[value^="2"]::-webkit-progress-bar,
.test[value^="3"]::-webkit-progress-bar 
{
 background-image: linear-gradient(red, red);
 background-size: 50% 50%;
 background-position: right top;
}

.test[value^="4"]::-webkit-progress-bar,
.test[value^="5"]::-webkit-progress-bar 
{
 background-image: linear-gradient(purple, purple);
 background-size: 50% 100%;
 background-position: right top;
}

.test[value^="6"]::-webkit-progress-bar,
.test[value^="7"]::-webkit-progress-bar,
.test[value="80"]::-webkit-progress-bar 
{
 background-image: linear-gradient(blue, blue), linear-gradient(blue, blue);
 background-size: 50% 100%, 50% 50%;
 background-position: right top, left bottom;
}



.test::-webkit-progress-bar, 
.test[value="2"]::-webkit-progress-bar, 
.test[value="3"]::-webkit-progress-bar, 
.test[value="4"]::-webkit-progress-bar, 
.test[value="5"]::-webkit-progress-bar, 
.test[value="6"]::-webkit-progress-bar, 
.test[value="7"]::-webkit-progress-bar, 
.test[value="8"]::-webkit-progress-bar {
 background-image: none;
 
} 

.test::-webkit-progress-value {
 background-color: green;
 height: 30%;
 transform-origin: left top;
 z-index: -1;
    position: absolute;
}

.test[value^="2"]::-webkit-progress-value,
.test[value^="3"]::-webkit-progress-value {
 background-color: red;
    top: -20%;
    left: 190%;
    transform: rotate(135deg) rotateX(-90deg) scaleX(3.9598);
}



.test[value^="4"]::-webkit-progress-value,
.test[value^="5"]::-webkit-progress-value {
 background-color: purple;
    left: 190%;
    top: 260%;
    transform: rotate(225deg) rotateX(-90deg) scaleX(3.9598);
}

.test[value^="6"]::-webkit-progress-value,
.test[value^="7"]::-webkit-progress-value,
.test[value="80"]::-webkit-progress-value {
 background-color: blue;
    left: -230%;
    top: 260%;
    transform: rotate(315deg) rotateX(-90deg) scaleX(3.9598);
}

.test::-webkit-progress-value, 
.test[value="2"]::-webkit-progress-value, 
.test[value="3"]::-webkit-progress-value, 
.test[value="4"]::-webkit-progress-value, 
.test[value="5"]::-webkit-progress-value, 
.test[value="6"]::-webkit-progress-value, 
.test[value="7"]::-webkit-progress-value, 
.test[value="8"]::-webkit-progress-value 
{
 background-color: green;
     left: 50%;
     top: -20%;
     transform: rotate(45deg) rotateX(-90deg) scaleX(3.9598);
}
<input id="input" type="range" value="0" min="0" max="80" onchange="change()" oninput="change()"/>
<input id="number" type="number" value="0" min="0" max="80" step="1" oninput="changeNumber()"/>
<div class="container">
<progress class="test" id="test" max="80" value="0"></progress>
</div>

overflow: hidden; 存在问题,并且Chrome中存在一个bug。它不应该在应用透视效果的同一元素上工作,但应该在进度本身上应用才能正常工作。它只有一半的时间有效...

另外,还有一个想法,样式更简单,我可以让它延伸到整个范围,但无论如何,这都是一个起点:

function change () {
    var input = document.getElementById("input");
    var progress = document.getElementById("test");
    progress.value = input.value;
}

function changeNumber () {
    var input = document.getElementById("number");
    var progress = document.getElementById("test");
    progress.value = input.value;
}
.test {
  width: 400px;
  height: 200px;
  margin: 10px 10px;
  border-radius: 9999px 9999px 0px 0px;
  border: solid 1px red;
  ackground-image: radial-gradient(lightblue 62%, transparent 40%);
  ox-shadow: 0px 0px 0px 500px lightblue;
  overflow: hidden;
}



.test::-webkit-progress-bar {
 background-color: transparent;
 position: relative;
    border-radius: 50%;
    perspective: 100px;
    perspective-origin: center 300px;
    z-index: -1;
 background-repeat: no-repeat;
}


.test::-webkit-progress-value {
 height: 300%;
 transform-origin: center bottom;
 bottom: -20%;
 z-index: -1;
    position: absolute;
 background-image: linear-gradient(270deg, red 2px, tomato 30px);
    transform:  rotateX(-90deg) scaleX(1);
}
<input id="input" type="range" value="0" min="0" max="80" onchange="change()" oninput="change()">
<input id="number" type="number" value="0" min="0" max="80" step="1" oninput="changeNumber()">
<progress class="test" id="test" max="80" value="20"></progress>


如果您想更新您的解决方案,请查看我的答案,我已经修复了Firefox的代码,在您将伪元素设置为透明后,您可以样式化进度本身。 - jcubic

7

运行我的代码并查看结果

.loader {
 position: relative;
 height: 100px;
 width: 100px;
 display: flex;
 align-items: center;
 justify-content: center;
 color: red;
 margin:30px 30px;
 float:left;
}
.loader:before {
 content: "";
 background: white;
 position:absolute;
 z-index:100;
 width:98px;
 height:98px;
 border-radius:50%;
 margin:auto auto;
}
progress::-moz-progress-bar { background: transparent; }
progress::-webkit-progress-bar {background: transparent;}
progress::-moz-progress-value { background: red; }
progress::-webkit-progress-value { background: red; }
.circle {
 border-radius: 100%;
 overflow: hidden;
 padding:0;
}
.spin {
 animation: spin 2s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
html {
 height: 100%;
 display: flex;
}
body {
 margin: auto;
}
<progress max="100" value="95" class="spin circle loader"></progress>

<progress max="100" value="50" class="spin circle loader"></progress>

<progress max="100" value="10" class="spin circle loader"></progress>

enter image description here

感谢@G-Cyr,我使用了他的一个答案的部分(这里),并将其与我的解决方案混合,使得这个答案更快。

好的解决方案,很遗憾你无法让线条更粗(看起来不太好)。 - jcubic

2

使用锥形渐变的解决方案(基于@RAM的解决方案-他的伪元素代码和来自@vals的交互式滑块想法),它使用CSS变量(自定义属性),因此您可以轻松更改颜色和大小。

输入图像描述

progress_number.addEventListener('change', function() {
  progress_input.value = progress.value = +progress_number.value;
});
progress_input.addEventListener('input', function() {
  progress_number.value = progress.value = +progress_input.value;
});
width_number.addEventListener('change', function() {
  width_input.value = width_number.value;
  progress_wrapper.style.setProperty('--width', width_number.value);
});
width_input.addEventListener('input', function() {
  width_number.value = width_input.value;
  progress_wrapper.style.setProperty('--width', width_input.value);
});
color.addEventListener('input', function() {
   progress.style.setProperty('--color', color.value);
});
background.addEventListener('input', function() {
   progress.style.setProperty('--background', background.value);
});
/* controls */
.grid {
   display: grid;
   grid-template-columns: 1fr 1fr;
   grid-template-rows: auto;
   grid-template-areas:
      "A B"
      "C D";
}
.grid label {
  width: 90px;
  display: inline-block;
  text-align: right;
}
.grid > :nth-child(1) {
  grid-area: A;
}
.grid > :nth-child(2) {
  grid-area: C;
}
.grid > :nth-child(3) {
  grid-area: B;
}
.grid > :nth-child(4) {
  grid-area: D;
}
:root {
  --color: #ff0000;
  --background: lightblue;
  --size: 100;
  --width: 10;
}
.progress {
    width: calc(var(--size) * 1px);
    height: calc(var(--size) * 1px);
    border-radius: 100%;
    overflow: hidden;
    padding: 0;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    color: transparent;
    background: transparent;
    margin: 30px 30px;
    float: left;
}
progress {
    height: 100%;
}
.progress:before {
    content: "";
    background: white;
    position:absolute;
    z-index:100;
    /* parenthesis are required */
    width: calc((var(--size) - (var(--width) * 2)) * 1px);
    height: calc((var(--size) - (var(--width) * 2)) * 1px);
    border-radius:50%;
    margin:auto auto;
}
progress::-moz-progress-value { background: transparent; }
progress::-webkit-progress-value { background: transparent; }
progress::-moz-progress-bar { background: transparent; }
/* CODE FOR GOOGLE CHROME GENERATED IN JS BY
[...new Array(100)].map((_, i) => `progress[value="${i}"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% ${i}%, var(--background) ${i}% 100%); }`).join('\n')
*/
progress[value="0"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 0%, var(--background) 0% 100%); }
progress[value="1"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 1%, var(--background) 1% 100%); }
progress[value="2"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 2%, var(--background) 2% 100%); }
progress[value="3"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 3%, var(--background) 3% 100%); }
progress[value="4"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 4%, var(--background) 4% 100%); }
progress[value="5"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 5%, var(--background) 5% 100%); }
progress[value="6"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 6%, var(--background) 6% 100%); }
progress[value="7"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 7%, var(--background) 7% 100%); }
progress[value="8"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 8%, var(--background) 8% 100%); }
progress[value="9"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 9%, var(--background) 9% 100%); }
progress[value="10"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 10%, var(--background) 10% 100%); }
progress[value="11"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 11%, var(--background) 11% 100%); }
progress[value="12"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 12%, var(--background) 12% 100%); }
progress[value="13"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 13%, var(--background) 13% 100%); }
progress[value="14"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 14%, var(--background) 14% 100%); }
progress[value="15"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 15%, var(--background) 15% 100%); }
progress[value="16"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 16%, var(--background) 16% 100%); }
progress[value="17"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 17%, var(--background) 17% 100%); }
progress[value="18"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 18%, var(--background) 18% 100%); }
progress[value="19"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 19%, var(--background) 19% 100%); }
progress[value="20"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 20%, var(--background) 20% 100%); }
progress[value="21"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 21%, var(--background) 21% 100%); }
progress[value="22"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 22%, var(--background) 22% 100%); }
progress[value="23"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 23%, var(--background) 23% 100%); }
progress[value="24"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 24%, var(--background) 24% 100%); }
progress[value="25"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 25%, var(--background) 25% 100%); }
progress[value="26"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 26%, var(--background) 26% 100%); }
progress[value="27"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 27%, var(--background) 27% 100%); }
progress[value="28"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 28%, var(--background) 28% 100%); }
progress[value="29"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 29%, var(--background) 29% 100%); }
progress[value="30"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 30%, var(--background) 30% 100%); }
progress[value="31"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 31%, var(--background) 31% 100%); }
progress[value="32"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 32%, var(--background) 32% 100%); }
progress[value="33"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 33%, var(--background) 33% 100%); }
progress[value="34"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 34%, var(--background) 34% 100%); }
progress[value="35"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 35%, var(--background) 35% 100%); }
progress[value="36"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 36%, var(--background) 36% 100%); }
progress[value="37"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 37%, var(--background) 37% 100%); }
progress[value="38"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 38%, var(--background) 38% 100%); }
progress[value="39"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 39%, var(--background) 39% 100%); }
progress[value="40"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 40%, var(--background) 40% 100%); }
progress[value="41"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 41%, var(--background) 41% 100%); }
progress[value="42"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 42%, var(--background) 42% 100%); }
progress[value="43"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 43%, var(--background) 43% 100%); }
progress[value="44"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 44%, var(--background) 44% 100%); }
progress[value="45"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 45%, var(--background) 45% 100%); }
progress[value="46"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 46%, var(--background) 46% 100%); }
progress[value="47"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 47%, var(--background) 47% 100%); }
progress[value="48"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 48%, var(--background) 48% 100%); }
progress[value="49"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 49%, var(--background) 49% 100%); }
progress[value="50"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 50%, var(--background) 50% 100%); }
progress[value="51"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 51%, var(--background) 51% 100%); }
progress[value="52"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 52%, var(--background) 52% 100%); }
progress[value="53"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 53%, var(--background) 53% 100%); }
progress[value="54"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 54%, var(--background) 54% 100%); }
progress[value="55"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 55%, var(--background) 55% 100%); }
progress[value="56"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 56%, var(--background) 56% 100%); }
progress[value="57"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 57%, var(--background) 57% 100%); }
progress[value="58"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 58%, var(--background) 58% 100%); }
progress[value="59"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 59%, var(--background) 59% 100%); }
progress[value="60"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 60%, var(--background) 60% 100%); }
progress[value="61"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 61%, var(--background) 61% 100%); }
progress[value="62"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 62%, var(--background) 62% 100%); }
progress[value="63"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 63%, var(--background) 63% 100%); }
progress[value="64"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 64%, var(--background) 64% 100%); }
progress[value="65"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 65%, var(--background) 65% 100%); }
progress[value="66"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 66%, var(--background) 66% 100%); }
progress[value="67"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 67%, var(--background) 67% 100%); }
progress[value="68"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 68%, var(--background) 68% 100%); }
progress[value="69"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 69%, var(--background) 69% 100%); }
progress[value="70"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 70%, var(--background) 70% 100%); }
progress[value="71"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 71%, var(--background) 71% 100%); }
progress[value="72"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 72%, var(--background) 72% 100%); }
progress[value="73"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 73%, var(--background) 73% 100%); }
progress[value="74"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 74%, var(--background) 74% 100%); }
progress[value="75"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 75%, var(--background) 75% 100%); }
progress[value="76"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 76%, var(--background) 76% 100%); }
progress[value="77"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 77%, var(--background) 77% 100%); }
progress[value="78"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 78%, var(--background) 78% 100%); }
progress[value="79"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 79%, var(--background) 79% 100%); }
progress[value="80"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 80%, var(--background) 80% 100%); }
progress[value="81"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 81%, var(--background) 81% 100%); }
progress[value="82"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 82%, var(--background) 82% 100%); }
progress[value="83"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 83%, var(--background) 83% 100%); }
progress[value="84"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 84%, var(--background) 84% 100%); }
progress[value="85"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 85%, var(--background) 85% 100%); }
progress[value="86"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 86%, var(--background) 86% 100%); }
progress[value="87"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 87%, var(--background) 87% 100%); }
progress[value="88"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 88%, var(--background) 88% 100%); }
progress[value="89"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 89%, var(--background) 89% 100%); }
progress[value="90"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 90%, var(--background) 90% 100%); }
progress[value="91"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 91%, var(--background) 91% 100%); }
progress[value="92"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 92%, var(--background) 92% 100%); }
progress[value="93"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 93%, var(--background) 93% 100%); }
progress[value="94"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 94%, var(--background) 94% 100%); }
progress[value="95"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 95%, var(--background) 95% 100%); }
progress[value="96"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 96%, var(--background) 96% 100%); }
progress[value="97"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 97%, var(--background) 97% 100%); }
progress[value="98"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 98%, var(--background) 98% 100%); }
progress[value="99"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% 99%, var(--background) 99% 100%); }
progress[value="100"]::-webkit-progress-bar { background: var(--color); }
/* STYLE FOR FIREFOX GENERATED IN JS BY
[...new Array(100)].map((_, i) => `progress[value="${i}"] { background: conic-gradient(var(--color) 0% ${i}%, var(--background) ${i}% 100%); }`).join('\n')
*/
progress[value="0"] { background: conic-gradient(var(--color) 0% 0%, var(--background) 0% 100%); }
progress[value="1"] { background: conic-gradient(var(--color) 0% 1%, var(--background) 1% 100%); }
progress[value="2"] { background: conic-gradient(var(--color) 0% 2%, var(--background) 2% 100%); }
progress[value="3"] { background: conic-gradient(var(--color) 0% 3%, var(--background) 3% 100%); }
progress[value="4"] { background: conic-gradient(var(--color) 0% 4%, var(--background) 4% 100%); }
progress[value="5"] { background: conic-gradient(var(--color) 0% 5%, var(--background) 5% 100%); }
progress[value="6"] { background: conic-gradient(var(--color) 0% 6%, var(--background) 6% 100%); }
progress[value="7"] { background: conic-gradient(var(--color) 0% 7%, var(--background) 7% 100%); }
progress[value="8"] { background: conic-gradient(var(--color) 0% 8%, var(--background) 8% 100%); }
progress[value="9"] { background: conic-gradient(var(--color) 0% 9%, var(--background) 9% 100%); }
progress[value="10"] { background: conic-gradient(var(--color) 0% 10%, var(--background) 10% 100%); }
progress[value="11"] { background: conic-gradient(var(--color) 0% 11%, var(--background) 11% 100%); }
progress[value="12"] { background: conic-gradient(var(--color) 0% 12%, var(--background) 12% 100%); }
progress[value="13"] { background: conic-gradient(var(--color) 0% 13%, var(--background) 13% 100%); }
progress[value="14"] { background: conic-gradient(var(--color) 0% 14%, var(--background) 14% 100%); }
progress[value="15"] { background: conic-gradient(var(--color) 0% 15%, var(--background) 15% 100%); }
progress[value="16"] { background: conic-gradient(var(--color) 0% 16%, var(--background) 16% 100%); }
progress[value="17"] { background: conic-gradient(var(--color) 0% 17%, var(--background) 17% 100%); }
progress[value="18"] { background: conic-gradient(var(--color) 0% 18%, var(--background) 18% 100%); }
progress[value="19"] { background: conic-gradient(var(--color) 0% 19%, var(--background) 19% 100%); }
progress[value="20"] { background: conic-gradient(var(--color) 0% 20%, var(--background) 20% 100%); }
progress[value="21"] { background: conic-gradient(var(--color) 0% 21%, var(--background) 21% 100%); }
progress[value="22"] { background: conic-gradient(var(--color) 0% 22%, var(--background) 22% 100%); }
progress[value="23"] { background: conic-gradient(var(--color) 0% 23%, var(--background) 23% 100%); }
progress[value="24"] { background: conic-gradient(var(--color) 0% 24%, var(--background) 24% 100%); }
progress[value="25"] { background: conic-gradient(var(--color) 0% 25%, var(--background) 25% 100%); }
progress[value="26"] { background: conic-gradient(var(--color) 0% 26%, var(--background) 26% 100%); }
progress[value="27"] { background: conic-gradient(var(--color) 0% 27%, var(--background) 27% 100%); }
progress[value="28"] { background: conic-gradient(var(--color) 0% 28%, var(--background) 28% 100%); }
progress[value="29"] { background: conic-gradient(var(--color) 0% 29%, var(--background) 29% 100%); }
progress[value="30"] { background: conic-gradient(var(--color) 0% 30%, var(--background) 30% 100%); }
progress[value="31"] { background: conic-gradient(var(--color) 0% 31%, var(--background) 31% 100%); }
progress[value="32"] { background: conic-gradient(var(--color) 0% 32%, var(--background) 32% 100%); }
progress[value="33"] { background: conic-gradient(var(--color) 0% 33%, var(--background) 33% 100%); }
progress[value="34"] { background: conic-gradient(var(--color) 0% 34%, var(--background) 34% 100%); }
progress[value="35"] { background: conic-gradient(var(--color) 0% 35%, var(--background) 35% 100%); }
progress[value="36"] { background: conic-gradient(var(--color) 0% 36%, var(--background) 36% 100%); }
progress[value="37"] { background: conic-gradient(var(--color) 0% 37%, var(--background) 37% 100%); }
progress[value="38"] { background: conic-gradient(var(--color) 0% 38%, var(--background) 38% 100%); }
progress[value="39"] { background: conic-gradient(var(--color) 0% 39%, var(--background) 39% 100%); }
progress[value="40"] { background: conic-gradient(var(--color) 0% 40%, var(--background) 40% 100%); }
progress[value="41"] { background: conic-gradient(var(--color) 0% 41%, var(--background) 41% 100%); }
progress[value="42"] { background: conic-gradient(var(--color) 0% 42%, var(--background) 42% 100%); }
progress[value="43"] { background: conic-gradient(var(--color) 0% 43%, var(--background) 43% 100%); }
progress[value="44"] { background: conic-gradient(var(--color) 0% 44%, var(--background) 44% 100%); }
progress[value="45"] { background: conic-gradient(var(--color) 0% 45%, var(--background) 45% 100%); }
progress[value="46"] { background: conic-gradient(var(--color) 0% 46%, var(--background) 46% 100%); }
progress[value="47"] { background: conic-gradient(var(--color) 0% 47%, var(--background) 47% 100%); }
progress[value="48"] { background: conic-gradient(var(--color) 0% 48%, var(--background) 48% 100%); }
progress[value="49"] { background: conic-gradient(var(--color) 0% 49%, var(--background) 49% 100%); }
progress[value="50"] { background: conic-gradient(var(--color) 0% 50%, var(--background) 50% 100%); }
progress[value="51"] { background: conic-gradient(var(--color) 0% 51%, var(--background) 51% 100%); }
progress[value="52"] { background: conic-gradient(var(--color) 0% 52%, var(--background) 52% 100%); }
progress[value="53"] { background: conic-gradient(var(--color) 0% 53%, var(--background) 53% 100%); }
progress[value="54"] { background: conic-gradient(var(--color) 0% 54%, var(--background) 54% 100%); }
progress[value="55"] { background: conic-gradient(var(--color) 0% 55%, var(--background) 55% 100%); }
progress[value="56"] { background: conic-gradient(var(--color) 0% 56%, var(--background) 56% 100%); }
progress[value="57"] { background: conic-gradient(var(--color) 0% 57%, var(--background) 57% 100%); }
progress[value="58"] { background: conic-gradient(var(--color) 0% 58%, var(--background) 58% 100%); }
progress[value="59"] { background: conic-gradient(var(--color) 0% 59%, var(--background) 59% 100%); }
progress[value="60"] { background: conic-gradient(var(--color) 0% 60%, var(--background) 60% 100%); }
progress[value="61"] { background: conic-gradient(var(--color) 0% 61%, var(--background) 61% 100%); }
progress[value="62"] { background: conic-gradient(var(--color) 0% 62%, var(--background) 62% 100%); }
progress[value="63"] { background: conic-gradient(var(--color) 0% 63%, var(--background) 63% 100%); }
progress[value="64"] { background: conic-gradient(var(--color) 0% 64%, var(--background) 64% 100%); }
progress[value="65"] { background: conic-gradient(var(--color) 0% 65%, var(--background) 65% 100%); }
progress[value="66"] { background: conic-gradient(var(--color) 0% 66%, var(--background) 66% 100%); }
progress[value="67"] { background: conic-gradient(var(--color) 0% 67%, var(--background) 67% 100%); }
progress[value="68"] { background: conic-gradient(var(--color) 0% 68%, var(--background) 68% 100%); }
progress[value="69"] { background: conic-gradient(var(--color) 0% 69%, var(--background) 69% 100%); }
progress[value="70"] { background: conic-gradient(var(--color) 0% 70%, var(--background) 70% 100%); }
progress[value="71"] { background: conic-gradient(var(--color) 0% 71%, var(--background) 71% 100%); }
progress[value="72"] { background: conic-gradient(var(--color) 0% 72%, var(--background) 72% 100%); }
progress[value="73"] { background: conic-gradient(var(--color) 0% 73%, var(--background) 73% 100%); }
progress[value="74"] { background: conic-gradient(var(--color) 0% 74%, var(--background) 74% 100%); }
progress[value="75"] { background: conic-gradient(var(--color) 0% 75%, var(--background) 75% 100%); }
progress[value="76"] { background: conic-gradient(var(--color) 0% 76%, var(--background) 76% 100%); }
progress[value="77"] { background: conic-gradient(var(--color) 0% 77%, var(--background) 77% 100%); }
progress[value="78"] { background: conic-gradient(var(--color) 0% 78%, var(--background) 78% 100%); }
progress[value="79"] { background: conic-gradient(var(--color) 0% 79%, var(--background) 79% 100%); }
progress[value="80"] { background: conic-gradient(var(--color) 0% 80%, var(--background) 80% 100%); }
progress[value="81"] { background: conic-gradient(var(--color) 0% 81%, var(--background) 81% 100%); }
progress[value="82"] { background: conic-gradient(var(--color) 0% 82%, var(--background) 82% 100%); }
progress[value="83"] { background: conic-gradient(var(--color) 0% 83%, var(--background) 83% 100%); }
progress[value="84"] { background: conic-gradient(var(--color) 0% 84%, var(--background) 84% 100%); }
progress[value="85"] { background: conic-gradient(var(--color) 0% 85%, var(--background) 85% 100%); }
progress[value="86"] { background: conic-gradient(var(--color) 0% 86%, var(--background) 86% 100%); }
progress[value="87"] { background: conic-gradient(var(--color) 0% 87%, var(--background) 87% 100%); }
progress[value="88"] { background: conic-gradient(var(--color) 0% 88%, var(--background) 88% 100%); }
progress[value="89"] { background: conic-gradient(var(--color) 0% 89%, var(--background) 89% 100%); }
progress[value="90"] { background: conic-gradient(var(--color) 0% 90%, var(--background) 90% 100%); }
progress[value="91"] { background: conic-gradient(var(--color) 0% 91%, var(--background) 91% 100%); }
progress[value="92"] { background: conic-gradient(var(--color) 0% 92%, var(--background) 92% 100%); }
progress[value="93"] { background: conic-gradient(var(--color) 0% 93%, var(--background) 93% 100%); }
progress[value="94"] { background: conic-gradient(var(--color) 0% 94%, var(--background) 94% 100%); }
progress[value="95"] { background: conic-gradient(var(--color) 0% 95%, var(--background) 95% 100%); }
progress[value="96"] { background: conic-gradient(var(--color) 0% 96%, var(--background) 96% 100%); }
progress[value="97"] { background: conic-gradient(var(--color) 0% 97%, var(--background) 97% 100%); }
progress[value="98"] { background: conic-gradient(var(--color) 0% 98%, var(--background) 98% 100%); }
progress[value="99"] { background: conic-gradient(var(--color) 0% 99%, var(--background) 99% 100%); }
progress[value="100"] { background: var(--color); }
<div class="grid">
<div>
<label>Progress:</label>
<input id="progress_input" type="range" value="20" min="0" max="100"/>
<input id="progress_number" type="number" value="20" min="0" max="100" step="1"/>
</div>
<div>
<label>Width:</label>
<input id="width_input" type="range" value="10" min="1" max="40"/>
<input id="width_number" type="number" value="10" min="1" max="40" step="1"/>
</div>
<div>
<label>Color:</label>
<input id="color" type="color" value="#ff0000"/>
</div>
<div>
<label>Background:</label>
<input id="background" type="color" value="#ADD8E6"/>
</div>
</div>
<div class="progress" id="progress_wrapper">
   <progress id="progress" value="20" max="100"></progress>
<div>

如果在 CSS 中找不到它,那么这个大部分是由 JavaScript 生成的。
// Google Chrome
[...new Array(100)].map((_, i) => `progress[value="${i}"]::-webkit-progress-bar { background: conic-gradient(var(--color) 0% ${i}%, var(--background) ${i}% 100%); }`).join('\n')

// Firefox
[...new Array(100)].map((_, i) => `progress[value="${i}"] { background: conic-gradient(var(--color) 0% ${i}%, var(--background) ${i}% 100%); }`).join('\n')

生成了1-99的进度,但只写入了完整的颜色:

/* Google Chrome full color */
progress[value="100"]::-webkit-progress-bar { background: var(--color); }
/* Firefox full color */
progress[value="100"] { background: var(--color); }

这是一个链接到CodePen的页面,我在这个解决方案中使用了它:https://codepen.io/jcubic/pen/yLgPpYQ 编辑: 如果你正在使用SCSS,可以使用以下代码生成CSS:
@for $percent from 0 through 99 {
    progress[value="#{$percent}"]::-webkit-progress-bar {
            background: conic-gradient(
        var(--progress-color) 0% #{$percent}%,
        var(--rest-color) #{$percent}% 100%);
    }
}
progress[value="100"]::-webkit-progress-bar { background: var(--progress-color); }

@for $percent from 0 through 99 {
    progress[value="#{$percent}"] {
        background: conic-gradient(
            var(--progress-color) 0% #{$percent}%,
            var(--rest-color) #{$percent}% 100%);
    }
}
progress[value="100"] {
    background: var(--progress-color);
}

1
@Flimm 更新了代码以在Firefox中运行。在Firefox和Chrome中,进度条的样式有些不一致。 - jcubic

2
这是一个有趣的挑战。

浏览器甚至操作系统都会给 元素应用一些默认样式。

<progress max="100" value="85"></progress>

首先,我们应该摆脱外观属性,将其设置为无。

progress {
   -webkit-appearance: none;
   -moz-appearance:    none;
   appearance:         none;
}
<progress max="100" value="85"></progress>

然后,浏览器通过叠加伪元素来创建额外的样式。例如,如果您在任何 WebKit 浏览器中查看此答案,则上面的片段仍将显示为一个带有绿色填充的平面框,表示进度。

这些伪元素也可以在 CSS 中进行处理。每个浏览器都有其特定的伪元素,这进一步复杂化了问题。

Webkit 堆叠了 3 个伪元素,其层次结构如下enter image description here

而 Gecko 和 Trident 则使用单个伪元素来表示进度条的填充,分别为 ::-moz-progress-bar::-ms-fill

progress {
/*gets rid of default appearance*/
   -webkit-appearance: none;
   -moz-appearance:    none;
   appearance:         none;
/*styles as any good ol' div would */
  border: 1px solid black;
  display:block;
  width:100px; height:100px;
  background:chartreuse;
}

/* gets rid of default pseudo-elements */
::-webkit-progress-inner-element {display:none}
/*for some reason, Firefox won't let the display or the content of this pseudo-element
set to none, so height:0 should do the trick. Maybe visibility:hidden too.*/
::-moz-progress-bar{height: 0;}
::-ms-fill {display:none; }
<progress max="100" value="85"></progress>

这将使我们使用已经定义好的 div 标签来样式化进度条元素,可以应用于上述任何一种圆形进度条方法,同时保持语义性。我们甚至可以使用默认的伪元素,并根据需要进行样式化,而不是创建嵌套的 div 等混乱的标记。
当然,这是高度实验性和非标准的,因此不应该用于生产环境。但支持程度相当不错,所有主要浏览器都支持 appearance 属性的某种形式,三个主要引擎也支持伪元素的样式化...所以也许我会撤回之前的声明,并改为“必须特别小心”。

1
我只是在@jcubic的想法基础上进行了重构,没有使用包装器。
<h4>Default</h4>
<div class="box">
  <progress id="pr" value="20" max="100"></progress>
  <progress id="pr" value="50" max="100"></progress>
  <progress id="pr" value="80" max="100"></progress>
  <progress id="pr" value="100" max="100"></progress>
</div>
<h4>Colors</h4>
<div class="box">
  <progress id="pr" value="20" max="100" style="--color: red"></progress>
  <progress id="pr" value="50" max="100" style="--color: orange"></progress>
  <progress id="pr" value="80" max="100" style="--color: green"></progress>
  <progress id="pr" value="100" max="100" style="--color: purple"></progress>
</div>
<h4>Thickness</h4>
<div class="box">
  <progress id="pr" value="20" max="100" style="--thickness: 10px"></progress>
  <progress id="pr" value="20" max="100" style="--thickness: 20px"></progress>
  <progress id="pr" value="20" max="100" style="--thickness: 30px"></progress>
  <progress id="pr" value="20" max="100" style="--thickness: 40px"></progress>
</div>
<h4>Sizes</h4>
<div class="box">
  <progress id="pr" value="20" max="100" style="--radius: 50px"></progress>
  <progress id="pr" value="20" max="100" style="--radius: 100px"></progress>
  <progress id="pr" value="20" max="100" style="--radius: 150px"></progress>
  <progress id="pr" value="20" max="100" style="--radius: 200px"></progress>
</div>

:root {
  --color: greenyellow;
  --track-color: #444;
  --background: #111a23;
}

body {
  background: var(--background);
  color: white;
}

.box {
  display: flex;
  gap: 1em;
}

progress {
  height: var(--radius, 100px);
  width: var(--radius, 100px);
  border-radius: 50%;
  overflow: hidden;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
progress::-webkit-progress-value {
  background: transparent;
}
progress::-moz-progress-bar {
  background: transparent;
}
progress:before {
  position: absolute;
  width: calc(var(--radius, 100px) - var(--thickness, 10px) * 2);
  height: calc(var(--radius, 100px) - var(--thickness, 10px) * 2);
  background-color: var(--background);
  color: white;
  margin: auto;
  content: attr(value) "%";
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

@for $percent from 0 through 99 {
  progress[value="#{$percent}"]::-webkit-progress-bar {
    background: conic-gradient(
      var(--color) 0% #{$percent}#{"%"},
      var(--track-color) #{$percent}#{"%"} 100%
    );
  }
}
progress[value="100"]::-webkit-progress-bar {
  background: var(--color);
}

@for $percent from 0 through 99 {
  progress[value="#{$percent}"] {
    background: conic-gradient(
      var(--color) 0% #{$percent}#{"%"},
      var(--track-color) #{$percent}#{"%"} 100%
    );
  }
}
progress[value="100"] {
  background: var(--color);
}

https://codepen.io/comfuture/pen/GRybrwq


使用这个解决方案可以进行值得的清理工作。 - horace

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接