javascript - 如何平滑地拉伸(stretch)和收缩旋转线弧?

标签 javascript css svg css-animations spinner

Google 的 Material Design 旋转器在旋转时收缩和拉伸(stretch):

enter image description here

enter image description here

enter image description here

我发现以下 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 执行一些任务,微调器会失去其平滑度并变得不稳定。

旋转(通过 transform)效果很好,我知道它运行在 UI 线程之外,所以即使我执行一些 JS 任务,旋转也会在动画时平滑。

当然,我可以只实现一个没有拉伸(stretch)/收缩功能的旋转微调器,但我想知道你们中是否有人知道如何使此类动画看起来始终流畅。有没有办法在旋转时使用 transform 拉伸(stretch)和收缩微调器?

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

最佳答案

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

这是一个示例,您可以在其中调整不同的值以获得所需的动画。为简单起见使用了 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>

关于javascript - 如何平滑地拉伸(stretch)和收缩旋转线弧?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57774970/

相关文章:

css - 动态填充导航

css - 布局与 IE7 冲突

css - 如何使用 CSS 将 div 中的图像垂直居中对齐?

javascript - 检查字符串是否由所有唯一字符组成——JavaScript 中的不等于运算符

java - 无法从 javascript 获取 json 到运行中的 java

Javascript - 为什么对象数组不等价?

javascript - 仅从不同的基本 url 获取子字符串

html - 是否可以制作 HTML/CSS 折线的一部分,贝塞尔曲线?

css - 当用作背景图像时,我的 SVG 图像忽略了它的样式表

html - 是否可以转义 SVG View 框? (或者如何将 HTML DIV 定位在缩放/转换后的 SVG 上?)