【问题标题】:How can I smoothly stretch and shrink a revolving line arc?如何平滑地拉伸和收缩旋转线弧?
【发布时间】:2020-01-06 13:19:15
【问题描述】:

Google 的 Material Design 微调器在旋转时会收缩和拉伸:

我在这里找到了以下 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 任务,在动画时旋转也会很平滑。

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

希望我很清楚。感谢关注!

【问题讨论】:

    标签: javascript css svg css-animations spinner


    【解决方案1】:

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

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

    【讨论】:

    • 酷!感谢您的快速答复。理解这些伪元素是如何工作的对我来说有点棘手。无论如何,您认为有没有办法避免为它们使用白色边框?
    • @tonix 很容易理解,让每个边框的颜色都不同,你会看到它们是如何移动的......实际上我认为没有白色的方法但我会继续思考;)
    • @tonix 发现了另一个想法,仍然有一个小缺点,但具有透明度;)
    • 非常感谢您的编辑和解释!现在我想我明白它是如何工作的了。如果我错了,请纠正我:在给定时间,所有伪元素都有0 度数的旋转。在hide 动画的50% 处,blue 边框保持在原来的位置,而redgreenyellow0 过渡到90180 和@ 987654337@度,分别在2 seconds。最后,从动画的50%100%,所有的条在2 other seconds 内都过渡到360 度数,此时(在4 seconds 之后)动画重新开始。我收到了吗?
    • @tonix 确切地说,在 50% 时它们将不再重叠,并且您将获得拉伸效果,然后在 0% 和 100% 时它们重叠,并且由于 0deg 等于 360deg,因此您有一个连续的动画然后使用主容器上的动画旋转所有这些。
    猜你喜欢
    • 1970-01-01
    • 2018-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多