如何平滑地拉伸和收缩旋转线弧?

How can I smoothly stretch and shrink a revolving line arc?

Google 的 Material 设计旋转器在旋转时收缩和拉伸:

我找到了以下 SVG 微调器,它在这里很好地实现了它:https://codepen.io/svnt/pen/qraaRN

HTML:

<svg class="spinner" viewBox="0 0 50 50"><circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle></svg>

CSS:

/* SVG spinner icon animation */
.spinner {
  -webkit-animation: rotate 2s linear infinite;
          animation: rotate 2s linear infinite;
  z-index: 2;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -25px 0 0 -25px;
  width: 50px;
  height: 50px;
}
.spinner .path {
  stroke: #cccccc;
  stroke-linecap: round;
  -webkit-animation: dash 1.5s ease-in-out infinite;
          animation: dash 1.5s ease-in-out infinite;
}

@-webkit-keyframes rotate {
  100% {
    -webkit-transform: rotate(360deg);
            transform: rotate(360deg);
  }
}

@keyframes rotate {
  100% {
    -webkit-transform: rotate(360deg);
            transform: rotate(360deg);
  }
}
@-webkit-keyframes dash {
  0% {
    stroke-dasharray: 1, 150;
    stroke-dashoffset: 0;
  }
  50% {
    stroke-dasharray: 90, 150;
    stroke-dashoffset: -35;
  }
  100% {
    stroke-dasharray: 90, 150;
    stroke-dashoffset: -124;
  }
}
@keyframes dash {
  0% {
    stroke-dasharray: 1, 150;
    stroke-dashoffset: 0;
  }
  50% {
    stroke-dasharray: 90, 150;
    stroke-dashoffset: -35;
  }
  100% {
    stroke-dasharray: 90, 150;
    stroke-dashoffset: -124;
  }
}

问题是,@keyframes 动画使用了 stroke-dasharraystroke-dashoffset,它们似乎在主 UI 线程上 运行 就好像我做了一些JavaScript 的任务,而动画 运行ning,微调器失去平滑度并变得不稳定。

旋转(通过 transform)工作得很好,我知道它 运行 离开了 UI 线程,所以即使我执行一些 JS 任务,旋转也会动画时流畅。

当然,我可以只实现一个没有 stretch/shrink 东西的旋转微调器,但我想知道你们中是否有人知道如何让这样的动画看起来总是很流畅。有没有办法在旋转时使用 transform 拉伸和收缩微调器?

希望我说清楚了。感谢关注!

您可以使用不同的元素来模拟这一点,其中的想法是使用另一个隐藏第一个元素。唯一的缺点是透明度。

这是一个示例,您可以在其中调整不同的值以获得所需的动画。为简单起见,使用了 CSS 变量,但这不是强制性的。

.loading {
  width:50px;
  height:50px;
  position:fixed;
  top:calc(50% - 25px);
  left:calc(50% - 25px);
  border-radius:50%;
  border:5px solid blue;
  animation:load 2s linear  infinite;
}
.loading:before,
.loading:after,
.loading span:before,
.loading span:after{
  content:"";
  position:absolute;
  top:-6px;
  left:-6px;
  right:-6px;
  bottom:-6px;
  border-radius:50%;
  border:7px solid transparent;
  border-left-color:white;
  animation:hide 1.2s infinite;
}
.loading span:before {
  --r:90deg;
}
.loading span:after {
  --r:180deg;
}
.loading:before {
  --r:260deg; /* a little less than 270deg to keep some border visible */
}

@keyframes load {
  to {
   transform:rotate(360deg);
  }
}

@keyframes hide {
  50% {
   transform:rotate(var(--r,0deg));
  }
  100% {
   transform:rotate(360deg);
  }
}
<div class="loading">
  <span></span>
</div>

有了透明度,您可以使用 4 个元素创建边框,您可以旋转这些元素使它们彼此重叠并缩小整体形状。基本上和第一段代码相反的逻辑(我们把蓝色改成透明,白色改成蓝色)

唯一的缺点是你不能收缩小于一侧的长度

.loading {
  width:50px;
  height:50px;
  position:fixed;
  top:calc(50% - 25px);
  left:calc(50% - 25px);
  animation:load 2s linear  infinite;
}
.loading:before,
.loading:after,
.loading span:before,
.loading span:after{
  content:"";
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  border-radius:50%;
  border:5px solid transparent;
  border-left-color:blue;
  animation:hide 1.2s infinite;
}
.loading span:before {
  --r:90deg;
}
.loading span:after {
  --r:180deg;
}
.loading:before {
  --r:200deg;
}


@keyframes load {
  to {
   transform:rotate(360deg);
  }
}

@keyframes hide {
  50% {
   transform:rotate(var(--r,0deg));
  }
  100% {
   transform:rotate(360deg);
  }
}

body {
 background:linear-gradient(to right,pink,orange);
}
<div class="loading">
  <span></span>
</div>


为了更好地理解这两个代码中发生的事情,删除主旋转并为边框使用不同的颜色

.loading {
  width:50px;
  height:50px;
  position:fixed;
  top:calc(50% - 25px);
  left:calc(50% - 25px);
 /* animation:load 2s linear  infinite;*/
}
.loading:before,
.loading:after,
.loading span:before,
.loading span:after{
  content:"";
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:-0;
  border-radius:50%;
  border:5px solid transparent;
  border-left-color:blue;
  animation:hide 4s infinite;
}
.loading span:before {
  --r:90deg;
    border-left-color:red;
}
.loading span:after {
  --r:180deg;
    border-left-color:green;
}
.loading:before {
  --r:260deg;
    border-left-color:yellow;
}


@keyframes load {
  to {
   transform:rotate(360deg);
  }
}

@keyframes hide {
  50% {
   transform:rotate(var(--r,0deg));
  }
  100% {
   transform:rotate(360deg);
  }
}

body {
 background:linear-gradient(to right,pink,orange);
}
<div class="loading">
  <span></span>
</div>