到目前为止,回答都非常好,特别是Temani Afif的text-indent技巧。我最初考虑了做类似的事情,但想要走一个稍微不同的方向。最终,我采用了一种解决方案,利用了新的CSS Houdini @property定义和一些counter-reset诡计,将数字CSS自定义属性转换为字符串,然后我们可以在添加的伪选择器的content属性中引用它们。以下是我的解决方案的完整代码片段,详细说明也在下面。
@property --progress-value {
syntax: "<integer>";
inherits: true;
initial-value: 0;
}
:root {
--progress-bar-color: #cfd8dc;
--progress-value-color: #2196f3;
--progress-empty-color-h: 4.1;
--progress-empty-color-s: 89.6;
--progress-empty-color-l: 58.4;
--progress-filled-color-h: 122.4;
--progress-filled-color-s: 39.4;
--progress-filled-color-l: 49.2;
}
html, body {
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: space-evenly;
flex-direction: column;
margin: 0;
font-family: "Roboto Mono", monospace;
}
progress[value] {
display: block;
position: relative;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
height: 6px;
border: 0;
--border-radius: 10px;
border-radius: var(--border-radius);
counter-reset: progress var(--progress-value);
--progress-value-string: counter(progress) "%";
--progress-max-decimal: calc(var(--value, 0) / var(--max, 0));
--progress-value-decimal: calc(var(--progress-value, 0) / var(--max, 0));
--progress-value-percent: calc(var(--progress-value-decimal) * 100%);
--progress-value-color: hsl(
calc((var(--progress-empty-color-h) + (var(--progress-filled-color-h) - var(--progress-empty-color-h)) * var(--progress-value-decimal)) * 1deg)
calc((var(--progress-empty-color-s) + (var(--progress-filled-color-s) - var(--progress-empty-color-s)) * var(--progress-value-decimal)) * 1%)
calc((var(--progress-empty-color-l) + (var(--progress-filled-color-l) - var(--progress-empty-color-l)) * var(--progress-value-decimal)) * 1%)
);
-webkit-animation: calc(1s * var(--progress-max-decimal)) ease-out 0s 1 normal both progress;
animation: calc(1s * var(--progress-max-decimal)) ease-out 0s 1 normal both progress;
}
@supports selector(::-moz-progress-bar) {
progress[value] {
--progress-value-decimal: calc(var(--value, 0) / var(--max, 0));
}
}
progress[value]::-webkit-progress-bar {
background-color: var(--progress-bar-color);
border-radius: var(--border-radius);
overflow: hidden;
}
progress[value]::-webkit-progress-value {
width: var(--progress-value-percent) !important;
background-color: var(--progress-value-color);
border-radius: var(--border-radius);
}
progress[value]::-moz-progress-bar {
width: var(--progress-value-percent) !important;
background-color: var(--progress-value-color);
border-radius: var(--border-radius);
}
progress[value]::after {
display: flex;
align-items: center;
justify-content: center;
--size: 32px;
width: var(--size);
height: var(--size);
position: absolute;
left: var(--progress-value-percent);
top: 50%;
transform: translate(-50%, -50%);
background-color: var(--progress-value-color);
border-radius: 50%;
content: attr(value);
content: var(--progress-value-string, var(--value));
font-size: 12px;
font-weight: 700;
color: #fff;
}
@-webkit-keyframes progress {
from {
--progress-value: 0;
} to {
--progress-value: var(--value);
}
}
@keyframes progress {
from {
--progress-value: 0;
} to {
--progress-value: var(--value);
}
}
<progress value="0" max="100" style="--value: 0; --max: 100;"></progress>
<progress value="25" max="100" style="--value: 25; --max: 100;"></progress>
<progress value="50" max="100" style="--value: 50; --max: 100;"></progress>
<progress value="75" max="100" style="--value: 75; --max: 100;"></progress>
<progress value="100" max="100" style="--value: 100; --max: 100;"></progress>
CodePen链接:cdpn.io/e/RwpyZGo
最终产品(截图,请在上面的代码片段底部点击“运行片段”以查看动画效果)。
详细解释
HTML已经内置了一个<progress>
元素,并包含几个伪元素,因此我真的想坚持使用它并围绕它进行样式设计。当与CSS Houdini的新@property
定义相结合时,这被证明是非常成功的,它允许我们创建更动态的动画等等。
事实上,Temani Afif发布了另一个很棒的答案,关于这个问题,他在这里写了一篇很棒的文章(We can finally animate CSS gradient by Temani Afif)。
使用新的@property
定义不仅允许我们动画化进度条的实际值,我们可以用它来改变进度条内进度值的宽度和%
标签,还可以在进度变化时生成动态颜色变化。
在我下面的示例中,我选择从红色到绿色过渡以表示进度。如果您希望使用单一颜色而不是这种变化的颜色,请将所有--progress-value-color
HSL值替换为单一颜色值。
同样地,我在animation
行中使用了一个calc()
来调整每个进度条动画的animation-duration
,以使它们以相同的速率移动,因此每个进度条不是同时开始和完成动画,而是在相同的时间通过相同的值。这意味着,如果两个进度条达到50%,其中一个的值为50%,那么该进度条将停止动画,而另一个进度条将继续动画到其新值。
如果您希望所有进度条都开始和结束的时间同步,只需用单个时间值(例如750毫秒、3秒等)替换那个
calc()
即可。请保留HTML标记。
@property --progress-value {
syntax: "<integer>";
inherits: true;
initial-value: 0;
}
:root {
--progress-bar-color: #cfd8dc;
--progress-value-color: #2196f3;
--progress-empty-color-h: 4.1;
--progress-empty-color-s: 89.6;
--progress-empty-color-l: 58.4;
--progress-filled-color-h: 122.4;
--progress-filled-color-s: 39.4;
--progress-filled-color-l: 49.2;
}
html, body {
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: space-evenly;
flex-direction: column;
margin: 0;
font-family: "Roboto Mono", monospace;
}
progress[value] {
display: block;
position: relative;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
height: 6px;
border: 0;
--border-radius: 10px;
border-radius: var(--border-radius);
counter-reset: progress var(--progress-value);
--progress-value-string: counter(progress) "%";
--progress-max-decimal: calc(var(--value, 0) / var(--max, 0));
--progress-value-decimal: calc(var(--progress-value, 0) / var(--max, 0));
--progress-value-percent: calc(var(--progress-value-decimal) * 100%);
--progress-value-color: hsl(
calc((var(--progress-empty-color-h) + (var(--progress-filled-color-h) - var(--progress-empty-color-h)) * var(--progress-value-decimal)) * 1deg)
calc((var(--progress-empty-color-s) + (var(--progress-filled-color-s) - var(--progress-empty-color-s)) * var(--progress-value-decimal)) * 1%)
calc((var(--progress-empty-color-l) + (var(--progress-filled-color-l) - var(--progress-empty-color-l)) * var(--progress-value-decimal)) * 1%)
);
-webkit-animation: calc(1s * var(--progress-max-decimal)) ease-out 0s 1 normal both progress;
animation: calc(1s * var(--progress-max-decimal)) ease-out 0s 1 normal both progress;
}
@supports selector(::-moz-progress-bar) {
progress[value] {
--progress-value-decimal: calc(var(--value, 0) / var(--max, 0));
}
}
progress[value]::-webkit-progress-bar {
background-color: var(--progress-bar-color);
border-radius: var(--border-radius);
overflow: hidden;
}
progress[value]::-webkit-progress-value {
width: var(--progress-value-percent) !important;
background-color: var(--progress-value-color);
border-radius: var(--border-radius);
}
progress[value]::-moz-progress-bar {
width: var(--progress-value-percent) !important;
background-color: var(--progress-value-color);
border-radius: var(--border-radius);
}
progress[value]::after {
display: flex;
align-items: center;
justify-content: center;
--size: 32px;
width: var(--size);
height: var(--size);
position: absolute;
left: var(--progress-value-percent);
top: 50%;
transform: translate(-50%, -50%);
background-color: var(--progress-value-color);
border-radius: 50%;
content: attr(value);
content: var(--progress-value-string, var(--value));
font-size: 12px;
font-weight: 700;
color: #fff;
}
@-webkit-keyframes progress {
from {
--progress-value: 0;
} to {
--progress-value: var(--value);
}
}
@keyframes progress {
from {
--progress-value: 0;
} to {
--progress-value: var(--value);
}
}
<progress value="0" max="100" style="--value: 0; --max: 100;"></progress>
<progress value="25" max="100" style="--value: 25; --max: 100;"></progress>
<progress value="50" max="100" style="--value: 50; --max: 100;"></progress>
<progress value="75" max="100" style="--value: 75; --max: 100;"></progress>
<progress value="100" max="100" style="--value: 100; --max: 100;"></progress>
CodePen链接:cdpn.io/e/RwpyZGo
对于每个进度条,我们需要声明value
和max
作为属性以及CSS自定义属性(变量),这显然并不理想。但是,CSSWG目前正在对attr()
进行几项改进,这将使我们很快能够以任何指定格式访问这些属性值,而无需像我在上面的示例中那样增加使用CSS自定义属性。
attr()
的浏览器支持
正如您可以在此处^从官方MDN关于attr()
的文档中看到的浏览器支持部分,目前对于attr()
的这些附加功能,如回退和类型或单位,支持非常有限。我们还需要能够在任何CSS属性(特别是CSS自定义属性)中使用attr()
,而不仅仅是content
属性,才能完全避免使用CSS自定义属性的解决方法。
这些改进目前处于“编辑草案”状态,没有生产浏览器支持,但这可能会在明年早些时候发生变化。因此,现在我们需要除了属性之外还要使用CSS自定义属性。另外,这个新的属性定义在Firefox中尚未得到支持,但我的解决方案包括一个@supports
查询回退,仍然可以确保进度条的宽度和颜色根据其值正确显示。
一旦所有这些CSS和Houdini更新在所有主要浏览器中都可用,希望明年就能实现所有这些,只需使用本机HTML属性即可:
<progress value="0" max="100"></progress>
<progress value="25" max="100"></progress>
<progress value="50" max="100"></progress>
<progress value="75" max="100"></progress>
<progress value="100" max="100"></progress>
在这种情况下,我们不再使用CSS自定义属性值--value
和--max
,而是可以通过以下方式在CSS中设置它们:
progress[value] {
--value: attr(value number);
}
progress[max] {
--max: attr(max number);
}
其余的逻辑将保持不变。关于attr()
的更多细节,请参考此处的MDN文档:attr()