【问题标题】:Animate svg with @keyframes on an elipsis path在省略号路径上使用 @keyframes 为 svg 制作动画
【发布时间】:2020-12-26 20:04:03
【问题描述】:

正如标题所说,我正在尝试为 svg 设置动画,使其看起来像省略号路径以匹配它 第一个图标(无穷大符号)。我试过使用变换,但动作很糟糕。我怎样才能以一种顺利(尽可能)的方式实现这一目标?

我发现了一些关于类似内容的文章,但它并没有让我得到我所追求的平滑省略号: https://www.useragentman.com/blog/2013/03/03/animating-circular-paths-using-css3-transitions/ http://thenewcode.com/860/Animating-Elements-In-Arcs-Circles-and-Ellipses-With-CSS https://usefulangle.com/post/32/moving-an-element-in-circular-path-with-css

这里是钢笔链接https://codepen.io/acubaniti/pen/abNqzEV?editors=1100

代码如下:

#gesture {
  width: 300px;
  height: 300px;
}

#phone {
  fill: white;
  animation: phone-orbits-cw 6s cubic-bezier(0.5, 0, 0.38, 0.1) infinite;
  opacity: 0;
  transform: rotate3d(0, 0, 0) translate3d(0, 0, 0);
  will-change: transform, opacity;
  transform-style: preserve-3d;
  transform-origin: center top;
}

#left, #right {
  fill: none;
  stroke-width: 10;
  stroke-miterlimit: 10;
  stroke: white;
  transform-origin: 200px 150px;
  animation-fill-mode: forwards;
  stroke-dasharray: 314;
  stroke-dashoffset: 314;
  opacity: 0;
}

#right {
  animation: circle-fill 6s linear infinite;
  stroke: white;
  transform: rotate(-180deg);
}

#left {
  animation: circle-fill-left 6s linear infinite;
  stroke: white;
  transform: rotateX(180deg);
}

@keyframes circle-fill {
  0%, 12.5% {
    stroke-dashoffset: 314;
    opacity: 1;
  }
  12.5%, 100% {
    stroke-dashoffset: 0;
  }
  37.5% {
    opacity: 1;
  }
  49%, 100% {
    opacity: 0;
  }
}
@keyframes circle-fill-left {
  12.5% {
    stroke-dashoffset: 314;
    opacity: 1;
  }
  25% {
    stroke-dashoffset: 0;
  }
  37.5% {
    opacity: 1;
  }
  49%, 100% {
    opacity: 0;
    stroke-dashoffset: 0;
  }
}
@keyframes phone-orbits-cw {
  0% {
    opacity: 0;
    transform: rotate3d(0, 0, 0) translate3d(0, 0, 0);
  }
  45% {
    opacity: 0;
    transform: rotate3d(0, 0, 1, -360deg) translate3d(2%, -5%, 0) rotate3d(0, 0, 1, 360deg);
  }
  58.75% {
    opacity: 1;
    transform: rotate3d(0, 0, 1, -360deg) translate3d(2%, -5%, 0) rotate3d(0, 0, 1, 360deg);
  }
  72.5% {
    opacity: 1;
  }
  86.25% {
    opacity: 1;
    transform: rotate3d(0, 0, 1, 360deg) translate3d(-2%, -5%, 0) rotate3d(0, 0, 1, -360deg);
  }
  100% {
    opacity: 1;
    transform: rotate3d(0, 0, 1, -360deg) translate3d(0, 0, 0) rotate3d(0, 0, 1, 360deg);
  }
}
@keyframes phone-orbits-ccw {
  0% {
    opacity: 0;
    transform: rotate3d(0, 0, 0) translate3d(0, 0, 0);
  }
  40% {
    opacity: 0;
    transform: rotate3d(0, 0, 1, 180deg) translate3d(0, 0, 0) rotate3d(0, 0, 1, -180deg);
  }
  45% {
    opacity: 1;
  }
  50% {
    opacity: 1;
    transform: rotate3d(0, 0, 1, -180deg) translate3d(2%, -5%, 0) rotate3d(0, 0, 1, 180deg);
  }
  75% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: rotate3d(0, 0, 1, 180deg) translate3d(0, 0, 0) rotate3d(0, 0, 1, -180deg);
  }
}
<html>
  <body style="background-color: black;"> 
    <svg id="gesture" viewBox="0 0 300 300" xml:space="preserve">
      <g id="phone">
        <path  d="M212.1 23.1H90.2c-4.8 0-8.8 3.9-8.8 8.8v236.4c0 4.8 3.9 8.8 8.8 8.8h121.9c4.8 0 8.8-3.9 8.8-8.8V31.8c0-4.8-4-8.7-8.8-8.7zm0 8.5c.1 0 .2.1.2.2v28.4H90.2V31.6h121.9zM90 268.2l.2-200.6h122.1l-.2 200.8-122.1-.2z"/>
        <circle cx="151.1" cy="248.2" r="8.8"/>
        <path d="M142 49.5h18.3c2.3 0 4.1-1.8 4.1-4.1s-1.8-4.1-4.1-4.1H142c-2.3 0-4.1 1.8-4.1 4.1s1.8 4.1 4.1 4.1z"/>
      </g>
      <circle id="right" cx="200" cy="150" r="50"/>
      <circle id="left" cx="100" cy="150" r="50"/>
    </svg> 
  </body>
</html>

谢谢。

【问题讨论】:

  • 堆栈片段不支持 SCSS。请发布编译后的 CSS。
  • 谢谢,我不知道。

标签: css svg css-animations keyframe


【解决方案1】:

如果您可以在浏览器兼容性方面做出妥协,您应该可以使用motion path 以更语义化的方式做到这一点。它需要 Chrome 64、Edge 79、Firefox 72、Opera 45,但不支持 Safari。

以下示例以多种方式简化了您的代码。首先,我将两个圆重写为一条路径,并另外移动它,使路径的起点和终点(“无穷大”符号的中点)位于坐标系的原点。这是因为这条路径将被用作运动路径 - 不是通过引用,不幸的是,如果您继续使用 CSS,这是不可能的,但是您可以复制并粘贴路径命令。

为了让stroke-dashoffset 动画让我的生活更轻松,路径还获取属性(不是 CSS 属性!)pathLength="100"。这基本上是一个任意值,这意味着:对于所有沿路径的距离计算,将 视为 100 是路径长度。

其次,我使用相同的路径命令定义了一个offset-pathoffset-rotate 设置为 0deg 以避免手机在移动时沿切线路径旋转。 offset-distance 在动画期间从 0 增加到 100%。

#gesture {
  width: 300px;
  height: 300px;
}

#phone {
  fill: white;
  animation: phone-orbit 6s ease-in-out infinite;
  opacity: 0;
  offset-path: path("M0 0 A 30 30 0 0 1 60 0 A 30 30 0 0 1 0 0 A 30 30 0 0 0 -60 0 A 30 30 0 0 0 0 0");
  offset-rotate: 0deg;
  offset-distance: 0;
  will-change: transform, opacity;
}

#infinity {
  fill: none;
  stroke-width: 6.666;
  stroke: white;
  animation-fill-mode: forwards;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  opacity: 0;
  animation: infinity-fill 6s linear infinite;
}

@keyframes infinity-fill {
  0%, 25% {
    stroke-dashoffset: 100;
    opacity: 1;
  }
  25%, 100% {
    stroke-dashoffset: 0;
  }
  37.5% {
    opacity: 1;
  }
  49%, 100% {
    opacity: 0;
  }
}
@keyframes phone-orbit {
  0% {
    opacity: 0;
  }
  40% {
    opacity: 0;
  }
  45% {
    opacity: 1;
  }
  50% {
    offset-distance: 0%;
  }
  100% {
    opacity: 1;
    offset-distance: 100%;
  }
}
<body style="background-color: black;"> 
    <svg id="gesture" viewBox="0 0 300 300" xml:space="preserve">
      <g id="phone">
        <path  d="M212.1 23.1H90.2c-4.8 0-8.8 3.9-8.8 8.8v236.4c0 4.8 3.9 8.8 8.8 8.8h121.9c4.8 0 8.8-3.9 8.8-8.8V31.8c0-4.8-4-8.7-8.8-8.7zm0 8.5c.1 0 .2.1.2.2v28.4H90.2V31.6h121.9zM90 268.2l.2-200.6h122.1l-.2 200.8-122.1-.2z"/>
        <circle cx="151.1" cy="248.2" r="8.8"/>
        <path d="M142 49.5h18.3c2.3 0 4.1-1.8 4.1-4.1s-1.8-4.1-4.1-4.1H142c-2.3 0-4.1 1.8-4.1 4.1s1.8 4.1 4.1 4.1z"/>
      </g>
      <path id="infinity" transform="translate(150 150) scale(1.666)" d="M0 0 A 30 30 0 0 1 60 0 A 30 30 0 0 1 0 0 A 30 30 0 0 0 -60 0 A 30 30 0 0 0 0 0"  pathLength="100"/>
    </svg> 
</body>

为了获得更好的支持,您可以切换到标记内SMIL animation。只有旧的 IE 版本会让你失望。

区别主要在于不同动画的启动方式:除了第一个 infinityFill 动画在 0 秒开始之外,每个后续动画都根据其运行时启动。尤其是phoneOrbit动画从infinityFill动画的结尾开始,然后phoneOrbit的结尾又开始infinityFill,以此类推。

此外,运动路径可以真正重复使用。

#gesture {
  width: 300px;
  height: 300px;
}

#phone {
  fill: white;
  opacity: 0;
  will-change: transform, opacity;
}

#infinity {
  fill: none;
    stroke-width: 10;
    stroke: white;
    stroke-dasharray: 100;
  opacity: 0;
}
<body style="background-color: black;"> 
    <svg id="gesture" viewBox="0 0 300 300">
      <defs>
        <path id="motion" d="M0 0 A 30 30 0 0 1 60 0 A 30 30 0 0 1 0 0 A 30 30 0 0 0 -60 0 A 30 30 0 0 0 0 0"  pathLength="100"/>
      </defs>
      <g id="phone">
        <path  d="M212.1 23.1H90.2c-4.8 0-8.8 3.9-8.8 8.8v236.4c0 4.8 3.9 8.8 8.8 8.8h121.9c4.8 0 8.8-3.9 8.8-8.8V31.8c0-4.8-4-8.7-8.8-8.7zm0 8.5c.1 0 .2.1.2.2v28.4H90.2V31.6h121.9zM90 268.2l.2-200.6h122.1l-.2 200.8-122.1-.2z"/>
        <circle cx="151.1" cy="248.2" r="8.8"/>
        <path d="M142 49.5h18.3c2.3 0 4.1-1.8 4.1-4.1s-1.8-4.1-4.1-4.1H142c-2.3 0-4.1 1.8-4.1 4.1s1.8 4.1 4.1 4.1z"/>
        <animateMotion id="phoneOrbit" dur="3s"
                       begin="infinityFill.end" rotate="0">
          <mpath href="#motion" />
        </animateMotion>
        <animate attributeName="opacity" dur="6s"
                 begin="infinityFill.begin"
                 values="0;0;1;1" keyTimes="0;.45;.5;1" />
      </g>
      <use id="infinity" href="#motion" transform="translate(150 150) scale(1.666)">
        <animate id="infinityFill" attributeName="stroke-dashoffset"
                 dur="3s" begin="0s;phoneOrbit.end"
                 values="100;0;0" keyTimes="0;.5;1" />
        <animate attributeName="opacity" dur="3s"
                 begin="infinityFill.begin"
                 values="1;1;0" keyTimes="0;.75;1" />
      </use>
    </svg> 
</body>

【讨论】:

  • 哇!非常感谢!
猜你喜欢
  • 1970-01-01
  • 2021-06-25
  • 1970-01-01
  • 2012-10-12
  • 2020-10-14
  • 1970-01-01
  • 2013-05-18
  • 2020-01-29
  • 1970-01-01
相关资源
最近更新 更多