如何从左到右制作圆形进度条动画?

时间:2019-06-23 19:37:46

标签: html css css-animations

我有一个圆形进度条,仅使用HTML和CSS,我使用关键帧进行加载(动画)。但是负载是从右到左,我想反转它。我编辑CSS关键帧,但什么也没做。我也尝试动画再反转一无所有。

提琴: https://jsfiddle.net/d20wu8e4/

我的结果(图片): https://ibb.co/0KCSsZY

我想要什么: https://ibb.co/MGCpHqS

* {
 box-sizing:border-box;
}
.progress {
  width: 150px;
  height: 150px;
  background: none;
  margin: 0 auto;
  box-shadow: none;
  position: relative;
}

.progress:after {
  content: "";
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 3px solid #fff;
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0.5;
}

.progress>span {
  width: 50%;
  height: 100%;
  overflow: hidden;
  position: absolute;
  top: 0;
  z-index: 1;
}

.progress .progress-left {
  left: 0;
}

.progress .progress-bar {
  width: 100%;
  height: 100%;
  background: none;
  border-width: 2px;
  border-style: solid;
  position: absolute;
  top: 0;
}

.progress .progress-left .progress-bar {
  left: 100%;
  border-top-right-radius: 80px;
  border-bottom-right-radius: 80px;
  border-left: 0;
  -webkit-transform-origin: center left;
  transform-origin: center left;
}

.progress .progress-right {
  right: 0;
}

.progress .progress-right .progress-bar {
  left: -100%;
  border-top-left-radius: 80px;
  border-bottom-left-radius: 80px;
  border-right: 0;
  -webkit-transform-origin: center right;
  transform-origin: center right;
  animation: loading 1.8s linear forwards;
}

.progress .progress-value {
  width: 79%;
  height: 79%;
  border-radius: 50%;
  background: none;
  font-size: 24px;
  color: black;
  line-height: 135px;
  text-align: center;
  position: absolute;
  top: 5%;
  left: 5%;
}

.progress.one .progress-bar {
  border-color: black;
}

.progress.one .progress-left .progress-bar {
  animation: loading-1 1s linear forwards 1.8s;
}

@keyframes loading {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(180deg);
    transform: rotate(180deg);
  }
}

@keyframes loading-1 {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(90deg);
    transform: rotate(90deg);
  }
}
<div class="container bg-danger">
  <div class="row mt-5">
    <div class="progress one">
      <span class="progress-left">
                <span class="progress-bar"></span>
      </span>
      <span class="progress-right ">
                <span class="progress-bar"></span>
      </span>
      <div class="progress-value">73%</div>
    </div>
  </div>
</div>

2 个答案:

答案 0 :(得分:4)

正如我评论的那样,简单的解决方案是旋转整个动画:

* {
 box-sizing:border-box;
}
.progress {
  width: 150px;
  height: 150px;
  background: none;
  margin: 0 auto;
  box-shadow: none;
  position: relative;
  transform: scaleX(-1);
}

.progress-value {
  transform: scaleX(-1);
}

.progress:after {
  content: "";
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 3px solid #fff;
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0.5;
}

.progress>span {
  width: 50%;
  height: 100%;
  overflow: hidden;
  position: absolute;
  top: 0;
  z-index: 1;
}

.progress .progress-left {
  left: 0;
}

.progress .progress-bar {
  width: 100%;
  height: 100%;
  background: none;
  border-width: 2px;
  border-style: solid;
  position: absolute;
  top: 0;
}

.progress .progress-left .progress-bar {
  left: 100%;
  border-top-right-radius: 80px;
  border-bottom-right-radius: 80px;
  border-left: 0;
  -webkit-transform-origin: center left;
  transform-origin: center left;
}

.progress .progress-right {
  right: 0;
}

.progress .progress-right .progress-bar {
  left: -100%;
  border-top-left-radius: 80px;
  border-bottom-left-radius: 80px;
  border-right: 0;
  -webkit-transform-origin: center right;
  transform-origin: center right;
  animation: loading 1.8s linear forwards;
}

.progress .progress-value {
  width: 79%;
  height: 79%;
  border-radius: 50%;
  background: none;
  font-size: 24px;
  color: black;
  line-height: 135px;
  text-align: center;
  position: absolute;
  top: 5%;
  left: 5%;
}

.progress.one .progress-bar {
  border-color: black;
}

.progress.one .progress-left .progress-bar {
  animation: loading-1 1s linear forwards 1.8s;
}

@keyframes loading {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(180deg);
    transform: rotate(180deg);
  }
}

@keyframes loading-1 {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(90deg);
    transform: rotate(90deg);
  }
}
<div class="progress one">
  <span class="progress-left">
                <span class="progress-bar"></span>
  </span>
  <span class="progress-right ">
                <span class="progress-bar"></span>
  </span>
  <div class="progress-value">73%</div>
</div>

顺便说一句,这是依赖更少代码的另一个想法。诀窍是考虑使用clip-path来调整不同点的位置,以创建所需的动画

.box {
  width:150px;
  height:150px;
  margin:20px;
  box-sizing:border-box;
  
  font-size:30px;
  display:flex;
  align-items:center;
  justify-content:center;
  position:relative;
  z-index:0;
}
.box:before {
  content:"";
  position:absolute;
  z-index:-1;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:5px solid #000;
  border-radius:50%;
  transform:rotate(45deg);
  clip-path:polygon(50% 50%,0 0,0 0,0 0, 0 0,0 0);
  animation:change 2s linear forwards;
}

@keyframes change {
  25% {
    clip-path:polygon(50% 50%,0 0,   0 100%,0 100%,0 100%,0 100%);
  }
  50% {
    clip-path:polygon(50% 50%,0 0,0 100%,   100% 100%, 100% 100%,100% 100%);
  }
  75% {
    clip-path:polygon(50% 50%,0 0,0 100%,100% 100%,    100% 0,100% 0);
  }
  100% {
    clip-path:polygon(50% 50%,0 0,0 100%,100% 100%, 100% 0,     0% 0%);
  }
}

body {
 background:pink;
}
<div class="box">
  73%
</div>

要更好地理解动画,请添加背景并删除半径。我们基本上在多边形中有6个点,其中2个是固定的(中心(50% 50%)和顶部一个(0 0)),然后移动其他4个将其放置在角落。诀窍是将它们一起移动,然后在每个角处保留一个(动画的每个状态)。

.box {
  width:100px;
  height:100px;
  margin:50px;
  box-sizing:border-box;
  
  font-size:30px;
  display:flex;
  align-items:center;
  justify-content:center;
  position:relative;
  z-index:0;
  background:rgba(0,0,0,0.5);
}
.box:before {
  content:"";
  position:absolute;
  z-index:-1;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:5px solid #000;
  background:red;
  transform:rotate(45deg);
  clip-path:polygon(50% 50%,0 0,0 0,0 0, 0 0,0 0);
  animation:change 5s linear forwards;
}

@keyframes change {
  25% {
    clip-path:polygon(50% 50%,0 0,   0 100%,0 100%,0 100%,0 100%);
  }
  50% {
    clip-path:polygon(50% 50%,0 0,0 100%,   100% 100%, 100% 100%,100% 100%);
  }
  75% {
    clip-path:polygon(50% 50%,0 0,0 100%,100% 100%,    100% 0,100% 0);
  }
  100% {
    clip-path:polygon(50% 50%,0 0,0 100%,100% 100%, 100% 0,     0% 0%);
  }

}

body {
 background:pink;
}
<div class="box">
  73%
</div>

使用此代码,您可以获得完整的动画,只需调整最终状态或删除某些状态即可将其停止在所需位置。

Ex达到75%(我们删除了最后一个状态)

.box {
  width:150px;
  height:150px;
  margin:20px;
  box-sizing:border-box;
  
  font-size:30px;
  display:flex;
  align-items:center;
  justify-content:center;
  position:relative;
  z-index:0;
}
.box:before {
  content:"";
  position:absolute;
  z-index:-1;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:5px solid #000;
  border-radius:50%;
  transform:rotate(45deg);
  clip-path:polygon(50% 50%,0 0,0 0,0 0, 0 0,0 0);
  animation:change 3s linear forwards;
}

@keyframes change {
  33% {
    clip-path:polygon(50% 50%,0 0,   0 100%,0 100%,0 100%,0 100%);
  }
  66% {
    clip-path:polygon(50% 50%,0 0,0 100%,   100% 100%, 100% 100%,100% 100%);
  }
  100% {
    clip-path:polygon(50% 50%,0 0,0 100%,100% 100%,    100% 0,100% 0);
  }
}
body {
 background:pink;
}
<div class="box">
  75%
</div>

使用66%(我们删除最后一个状态,然后更改第三个状态的百分比)

.box {
  width:150px;
  height:150px;
  margin:20px;
  box-sizing:border-box;
  
  font-size:30px;
  display:flex;
  align-items:center;
  justify-content:center;
  position:relative;
}
.box:before {
  content:"";
  position:absolute;
  z-index:-1;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:5px solid #000;
  border-radius:50%;
  transform:rotate(45deg);
  clip-path:polygon(50% 50%,0 0,0 0,0 0, 0 0,0 0);
  animation:change 2s linear forwards;
}

@keyframes change {
  33% {
    clip-path:polygon(50% 50%,0 0,   0 100%,0 100%,0 100%,0 100%);
  }
  66% {
    clip-path:polygon(50% 50%,0 0,0 100%,   100% 100%, 100% 100%,100% 100%);
  }
  100% {
    clip-path:polygon(50% 50%,0 0,0 100%,100% 100%,    100% 0,100% 40%);
  }
}
<div class="box">
  75%
</div>

具有10%(仅一种状态)

.box {
  width:150px;
  height:150px;
  margin:20px;
  box-sizing:border-box;
  
  font-size:30px;
  display:flex;
  align-items:center;
  justify-content:center;
  position:relative;
}
.box:before {
  content:"";
  position:absolute;
  z-index:-1;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border:5px solid #000;
  border-radius:50%;
  transform:rotate(45deg);
  clip-path:polygon(50% 50%,0 0,0 0,0 0, 0 0,0 0);
  animation:change 1s linear forwards;
}

@keyframes change {
  100% {
    clip-path:polygon(50% 50%,0 0,   0 40%,0 40%,0 40%,0 40%);
  }
}
body {
 background:pink;
}
<div class="box">
  10%
</div>

答案 1 :(得分:2)

此进度在使用conic-gradient()的情况下在新的眨眼/ Webkit浏览器中有效。另外,要更改进度,我们使用css变量,因此动画需要JS。

这个想法是创建一个黑色到透明的圆锥形渐变,并根据进度更改度数。为了得到一条线而不是一个圆,我使用了一个从白色到白色的内部渐变,该渐变不覆盖@TemaniAfif建议的边框(background-clip: content-box)。

播放输入框的值以查看进度。

const progress = document.querySelector('.circular-progress')

const updateProgress = value => {
  progress.style.setProperty('--percentage', `${value * 3.6}deg`)
  progress.innerText = `${value}%`
}

updateProgress(36)

document.querySelector('input')
  .addEventListener('input', e => {
    updateProgress(e.currentTarget.value)
  })
.circular-progress {
  display: flex;
  width: 150px;
  height: 150px;
  border:5px solid transparent;
  border-radius: 50%;
  align-items: center;
  justify-content: center;
  font-size: 1.5em;
  background:
    linear-gradient(#fff, #fff) content-box no-repeat,
    conic-gradient(black var(--percentage,0), transparent var(--percentage,0)) border-box; 
  --percentage: 0deg;
}
<div class="circular-progress"></div>

<br />

Progress value: <input type="number" min="0" max="100" value="36">

另一个方向(由@TemaniAfif添加):

const progress = document.querySelector('.circular-progress')

const updateProgress = value => {
  progress.style.setProperty('--percentage', `${value * 3.6}deg`)
  progress.innerText = `${value}%`
}

updateProgress(36)

document.querySelector('input')
  .addEventListener('input', e => {
    updateProgress(e.currentTarget.value)
  })
.circular-progress {
  display: flex;
  width: 150px;
  height: 150px;
  border:5px solid transparent;
  border-radius: 50%;
  align-items: center;
  justify-content: center;
  font-size: 1.5em;
  background:
    linear-gradient(#fff, #fff) content-box no-repeat,
    conic-gradient(from calc(-1*var(--percentage)), black var(--percentage,0), transparent var(--percentage,0)) border-box; 
  --percentage: 0deg;
}
<div class="circular-progress"></div>

<br />

Progress value: <input type="number" min="0" max="100" value="36">

同一想法的一种变体是创建具有多种颜色的进度圈,然后使用从透明到白色的渐变将其隐藏。增大透明区域以露出彩色线条。

const progress = document.querySelector('.circular-progress')

const updateProgress = value => {
  progress.style.setProperty('--percentage', `${value * 3.6}deg`)
  progress.innerText = `${value}%`
}

updateProgress(80)

document.querySelector('input')
  .addEventListener('input', e => {
    updateProgress(e.currentTarget.value)
  })
.circular-progress {
  display: flex;
  width: 150px;
  height: 150px;
  border: 5px solid transparent;
  border-radius: 50%;
  align-items: center;
  justify-content: center;
  font-size: 1.5em;
  background: 
    linear-gradient(#fff, #fff) content-box no-repeat,
    conic-gradient(transparent var(--percentage, 0), white var(--percentage, 0)) border-box,
    conic-gradient(green 120deg, yellow 120deg 240deg, red 240deg) border-box;
  --percentage: 0deg;
}
<div class="circular-progress"></div>

<br /> Progress value: <input type="number" min="0" max="100" value="80">