仅使用 CSS 的复杂比例三元进度条设计

Sam*_*Sam 10 html css flexbox progress-bar css-calc

我制作了一个自定义进度条,由三个单独的部分组成(一个独特的可定制中心部分、一个左侧部分和一个右侧部分),但我很难在所有阶段正确对齐中心块。

首先,我将使用三个图形布局显示所需的最终状态,然后我将描述当前的问题,最后我将提供我当前的解决方法 hack,这是有问题的,需要某种修复。


三个期望
状态:显示1%左对齐的起始状态的期望结果:
在此处输入图片说明

中间块完美位于中间的中途状态的预期结果50%
在此处输入图片说明

中心块完全停在100%右对齐的所需最终状态:
在此处输入图片说明


body{margin: 100px; background: #CCC}

.fullbar{
    background-color: blue;
    width: 100%;
}

.progress{
    background: green;
  margin: 10px 0 0 0;
    text-align: right;
    padding: 0 0px 1px 0px;
    line-height: 5px;
}

.number{
  background: inherit;
  color: #FFF;
    padding: 4px;
    padding: 0 2px 1px 3px;
 
}
Run Code Online (Sandbox Code Playgroud)
<div class="fullbar">
<div class="progress" style="width:50%">
    <div class="number">50%</div>
</div>
</div>
Run Code Online (Sandbox Code Playgroud)


问题
当状态为 50% 时,中心块应该在中间完美地水平对齐。然而事实并非如此。行尾居中,而不是包含实际数字“50%”的 div。

在此处输入图片说明


附注。出于未知原因,中心块的主体未在代码视图中正确呈现。也许我大量的 css 重置使我的进度条看起来与这里的裸代码不同。但它关于类名的 divnumber需要正确居中,目前还没有。


我哈克解决方案,工作不正常和不优雅
我试图用包裹的中心部分width:112%作为一个黑客以进度条,得到中央嵌段完美中间,就像这样:

<div class="fullbar">
  <div style="width:112%">
    <div class="progress" style="width:50%">
      <div class="number">50%</div>
    </div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

然而,虽然这确实使50%shortontally 看起来完美地居中,但最终状态100%现在被推到右侧的 div 边界之外,使解决方案不正确且无法使用。


主要问题和第一个悬赏 (50 分)
找到另一种 CSS(flex 或 calc)解决方案会很好,其中所有三个理想状态(见上面三张图片)完美对齐,状态适合开始状态,结束状态,以及“按比例”之间的所有内容。


奖金问题和第二个悬赏 (100 分)
A 部分)一种优雅的方式来动画进度(中心部分和左侧彩色条),仅使用 CSS,具有缓入缓出运动,页面延迟 1 秒加载。

B 部分)动画(和数字)应该从 0% 开始,中心部分的显示数字然后在动画过程中增长到 XX%(无论在 html 中设置为 %)并以正确的进度数字和正确的水平结束进度位置。

Tem*_*fif 13

你可以像下面这样做。我使用不同的颜色来更好地查看结果

body {
  margin: 100px;
  background: #CCC
}

.fullbar {
  background-color: blue;
}

.progress {
  background: lightgreen;
  margin: 10px 0 0 0;
  height: 5px;
  position:relative; /* relative here */
  width:var(--p);
}

.number {
  position:absolute; /* absolute here */
  background: rgba(255,0,0,0.5);
  left:100%; /* push to the right side */
  transform:translateX(calc(-1*var(--p))); /* offset to the left based on --p */
  top:-10px;
  bottom:-10px;
  color: #FFF;
  padding: 0 2px 1px 3px;
}
Run Code Online (Sandbox Code Playgroud)
<div class="fullbar">
  <div class="progress" style="--p:0%">
    <div class="number">0%</div>
  </div>
</div>

<div class="fullbar">
  <div class="progress" style="--p:20%">
    <div class="number">20%</div>
  </div>
</div>

<div class="fullbar">
  <div class="progress" style="--p:50%">
    <div class="number">50%</div>
  </div>
</div>

<div class="fullbar">
  <div class="progress" style="--p:80%">
    <div class="number">80%</div>
  </div>
</div>

<div class="fullbar">
  <div class="progress" style="--p:100%">
    <div class="number">100%</div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

仅使用一个 div 的另一个想法:

body {
  margin: 100px;
  background: #CCC
}

.progress {
  margin: 20px 0;
  height: 10px;
  position: relative;
  background: linear-gradient(lightgreen 0 0) 0/var(--p) 100% no-repeat blue;
}

.progress::before {
  content: attr(style);
  font-family: monospace;
  font-size:20px;
  white-space:nowrap;
  text-indent: -4ch;
  overflow: hidden;
  position: absolute;
  background: rgba(255, 0, 0, 0.8);
  border:5px solid transparent;
  top:50%;
  left: var(--p);
  transform: translate(calc(-1*var(--p)),-50%);
  color: #FFF;
}
Run Code Online (Sandbox Code Playgroud)
<div class="progress" style="--p:0%"></div>

<div class="progress" style="--p:20%"></div>

<div class="progress" style="--p:50%"></div>


<div class="progress" style="--p:80%"></div>

<div class="progress" style="--p:100%"></div>
Run Code Online (Sandbox Code Playgroud)

更新

带动画:

body {
  margin: 100px;
  background: #CCC
}

.progress {
  margin: 20px 0;
  height: 10px;
  position: relative;
  background: linear-gradient(lightgreen 0 0) 0/var(--p) 100% no-repeat blue;
  animation:p1 1s 1s both;
}

.progress::before {
  content: attr(style);
  font-family: monospace;
  font-size:20px;
  white-space:nowrap;
  text-indent: -4ch;
  overflow: hidden;
  position: absolute;
  background: rgba(255, 0, 0, 0.8);
  border:5px solid transparent;
  top:50%;
  left: var(--p);
  transform: translate(calc(-1*var(--p)),-50%);
  color: #FFF;
  animation:p2 1s 1s both;
}
@keyframes p1 {from {background-size:0 100%}}
@keyframes p2 {from {left:0;transform: translate(0%,-50%)}}
Run Code Online (Sandbox Code Playgroud)
<div class="progress" style="--p:0%"></div>

<div class="progress" style="--p:20%"></div>

<div class="progress" style="--p:50%"></div>

<div class="progress" style="--p:80%"></div>

<div class="progress" style="--p:100%"></div>
Run Code Online (Sandbox Code Playgroud)

对于我将使用的数字动画,@property但目前仅在 chrome an edge 上可用:

body {
  margin: 100px;
  background: #CCC
}

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

.progress {
  margin: 20px 0;
  height: 10px;
  position: relative;
  background: linear-gradient(lightgreen 0 0) 0/calc(var(--p,0)*1%) 100% no-repeat blue;
  animation:p1 1s 1s both;
  --s:var(--p);
  counter-set:num var(--s);
}

.progress::before {
  content: counter(num) "%";
  font-family: monospace;
  font-size:20px;
  white-space:nowrap;
  overflow: hidden;
  position: absolute;
  background: rgba(255, 0, 0, 0.8);
  border:5px solid transparent;
  top:50%;
  left: calc(var(--p)*1%);
  transform: translate(calc(-1%*var(--p)),-50%);
  color: #FFF;
}
@keyframes p1 {from {--p:0;--s:0}}
Run Code Online (Sandbox Code Playgroud)
<div class="progress" style="--p:0"></div>

<div class="progress" style="--p:20"></div>

<div class="progress" style="--p:50"></div>


<div class="progress" style="--p:80"></div>

<div class="progress" style="--p:100"></div>
Run Code Online (Sandbox Code Playgroud)

在有更多支持之前,您可以像下面那样伪造它:

body {
  margin: 100px;
  background: #CCC
}

.progress {
  margin: 20px 0;
  height: 10px;
  position: relative;
  background: linear-gradient(lightgreen 0 0) 0/var(--p) 100% no-repeat blue;
  animation:p1 1s 1s both;
}

.progress::before {
  content: attr(style);
  font-family: monospace;
  font-size:20px;
  white-space:nowrap;
  text-indent: -4ch;
  overflow: hidden;
  position: absolute;
  background: rgba(255, 0, 0, 0.8);
  border:5px solid transparent;
  top:50%;
  left: var(--p);
  transform: translate(calc(-1*var(--p)),-50%);
  color: #FFF;
  animation:p2 1s 1s both,p3 0.8s 1s both;
}
@keyframes p1 {from {background-size:0% 100%}}
@keyframes p2 {from {left:0%;transform: translate(0%,-50%)}}
@keyframes p3 { /* put some randome number to fake the animation*/
  0%  {content:"--p:0%"}
  15% {content:"--p:5%"}
  30% {content:"--p:9%"}
  45% {content:"--p:10%"}
  60% {content:"--p:11%"}
  75% {content:"--p:40%"}
  90% {content:"--p:20%"}
}
Run Code Online (Sandbox Code Playgroud)
<div class="progress" style="--p:0%"></div>

<div class="progress" style="--p:20%"></div>

<div class="progress" style="--p:50%"></div>


<div class="progress" style="--p:80%"></div>

<div class="progress" style="--p:100%"></div>
Run Code Online (Sandbox Code Playgroud)

或者像下面这样的疯狂想法:

body {
  margin: 100px;
  background: #CCC
}

.progress {
  margin: 20px 0;
  height: 10px;
  position: relative;
  background: linear-gradient(lightgreen 0 0) 0/calc(var(--p)*1%) 100% no-repeat blue;
  animation:p1 1s 1s both;
}

.progress::before {
  content: "0% \A 1% \A 2% \A 3% \A 4% \A 5% \A 6% \A 7% \A 8% \A 9% \A 10% \A 11% \A 12% \A 13% \A 14% \A 15% \A 16% \A 17% \A 18% \A 19% \A 20% \A 21% \A 22% \A 23% \A 24% \A 25% \A 26% \A 27% \A 28% \A 29% \A 30% \A 31% \A 32% \A 33% \A 34% \A 35% \A 36% \A 37% \A 38% \A 39% \A 40% \A 41% \A 42% \A 43% \A 44% \A 45% \A 46% \A 47% \A 48% \A 49% \A 50% \A 51% \A 52% \A 53% \A 54% \A 55% \A 56% \A 57% \A 58% \A 59% \A 60% \A 61% \A 62% \A 63% \A 64% \A 65% \A 66% \A 67% \A 68% \A 69% \A 70% \A 71% \A 72% \A 73% \A 74% \A 75% \A 76% \A 77% \A 78% \A 79% \A 80% \A 81% \A 82% \A 83% \A 84% \A 85% \A 86% \A 87% \A 88% \A 89% \A 90% \A 91% \A 92% \A 93% \A 94% \A 95% \A 96% \A 97% \A 98% \A 99% \A 100%";
  font-family: monospace;
  font-size:20px;
  width:4ch;
  line-height:1em;
  height:1em;
  text-align:center;
  overflow: hidden;
  position: absolute;
  background: rgba(255, 0, 0, 0.8);
  border:5px solid transparent;
  top:50%;
  left: calc(var(--p)*1%);
  transform: translate(calc(-1%*var(--p)),-50%);
  color: #0000;
  text-shadow:0 calc(var(--p)*-1em) 0 #fff;
  animation:p2 1s 1s both,p3 1s 1s steps(var(--p)) both;
}
@keyframes p1 {from {background-size:0% 100%}}
@keyframes p2 {from {left:0%;transform: translate(0%,-50%)}}
@keyframes p3 {from {text-shadow:0 0 0 #fff}}
Run Code Online (Sandbox Code Playgroud)
<div class="progress" style="--p:0"></div>

<div class="progress" style="--p:20"></div>

<div class="progress" style="--p:50"></div>

<div class="progress" style="--p:80"></div>

<div class="progress" style="--p:100"></div>
Run Code Online (Sandbox Code Playgroud)