【问题标题】:How to make a smooth dashed border rotation animation like 'marching ants'如何制作平滑的虚线边框旋转动画,如“行军蚂蚁”
【发布时间】:2015-04-25 22:47:49
【问题描述】:

我正在制作一个使用“齿轮和链条”的 CSS 动画,但无法创建“平滑”边框旋转序列。

您可以在fiddle 中看到我(目前)如何使用伪元素来生成“旋转”效果。这是通过在白色虚线和金色虚线边框之间“切换”来完成的,使其看起来像“边框在旋转”。

我有什么

#one{
  -webkit-animation: rotateClockwiseAnimation 5s linear infinite; /* Safari 4+ */
  -moz-animation:    rotateClockwiseAnimation 5s linear infinite; /* Fx 5+ */
  -o-animation:      rotateClockwiseAnimation 5s linear infinite; /* Opera 12+ */
  animation:         rotateClockwiseAnimation 5s linear infinite; /* IE 10+, Fx 29+ */

}
#two{
  -webkit-animation: rotateAntiClockwiseAnimation 5s linear infinite; /* Safari 4+ */
  -moz-animation:    rotateAntiClockwiseAnimation 5s linear infinite; /* Fx 5+ */
  -o-animation:      rotateAntiClockwiseAnimation 5s linear infinite; /* Opera 12+ */
  animation:         rotateAntiClockwiseAnimation 5s linear infinite; /* IE 10+, Fx 29+ */

 position:absolute;
    top:30px;
    left:42px;
    width:80px;
}

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

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

/******************************************************************************/

.chain{
    height:70px;
    width:80%;
    border:5px dashed gold;
    border-radius:30px;
    position:absolute;
    top:30px;
    left:40px;
          -webkit-animation: switchGoldBlackBorder 0.8s infinite; /* Safari 4+ */
  -moz-animation:    switchGoldBlackBorder 0.8s infinite; /* Fx 5+ */
  -o-animation:      switchGoldBlackBorder 0.8s infinite; /* Opera 12+ */
  animation:         switchGoldBlackBorder 0.8s infinite; /* IE 10+, Fx 29+ */
}


@-webkit-keyframes switchBlackGoldBorder {
    0%   { border: 5px dashed transparent; }
    49%   { border: 5px dashed transparent; }
    50%   { border: 5px dashed gold; }
    100%   { border: 5px dashed gold; }
}
@-moz-keyframes switchBlackGoldBorder{
    0%   { border: 5px dashed transparent; }
    49%   { border: 5px dashed transparent; }
    50%   { border: 5px dashed gold; }
    100%   { border: 5px dashed gold; }
}
@-o-keyframes switchBlackGoldBorder {
    0%   { border: 5px dashed transparent; }
    49%   { border: 5px dashed transparent; }
    50%   { border: 5px dashed gold; }
    100%   { border: 5px dashed gold; }
}
@keyframes switchBlackGoldBorder {  
    0%   { border: 5px dashed transparent; }
    49%   { border: 5px dashed transparent; }
    50%   { border: 5px dashed gold; }
    100%   { border: 5px dashed gold; }
}



.chain:after{
    content:"";
    position:absolute;
    height:70px;
    border-radius:30px;
    width:100%;
    top:-5px;
    left:-5px;
    border:5px solid gold;
    z-index:-1;
          -webkit-animation: switchBlackGoldBorder 0.8s infinite; /* Safari 4+ */
  -moz-animation:    switchBlackGoldBorder 0.8s infinite; /* Fx 5+ */
  -o-animation:      switchBlackGoldBorder 0.8s infinite; /* Opera 12+ */
  animation:         switchBlackGoldBorder 0.8s infinite; /* IE 10+, Fx 29+ */
}

@-webkit-keyframes switchGoldBlackBorder {
  0%   { border: 5px solid gold; }
    49%   { border: 5px solid gold; }
    50%   { border: 5px solid white; }
    100%   { border: 5px solid white; }
}
@-moz-keyframes switchGoldBlackBorder{
  0%   { border: 5px solid gold; }
    49%   { border: 5px solid gold; }
    50%   { border: 5px solid white; }
    100%   { border: 5px solid white; }
}
@-o-keyframes switchGoldBlackBorder {
  0%   { border: 5px solid gold; }
    49%   { border: 5px solid gold; }
    50%   { border: 5px solid white; }
    100%   { border: 5px solid white; }
}
@keyframes switchGoldBlackBorder {  
    0%   { border: 5px solid gold; }
    49%   { border: 5px solid gold; }
    50%   { border: 5px solid white; }
    100%   { border: 5px solid white; }
}
<svg id="one" style="width:50px" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 100">
    <defs>
        <circle id="c" cx="50" cy="50" r="30" stroke="#808080" fill="none" stroke-width="25"/>
        <path id="d" stroke="#808080" stroke-width="16" d="M50 0, V15 M50 100, V85 M0 50, H15 M100 50, H85"/>
    </defs>    
    <use xlink:href="#c"/>
    <use xlink:href="#d"/>
    <use xlink:href="#d" transform="rotate(45, 50, 50)"/>
</svg>

<svg id="two" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 100">
    <use xlink:href="#one"/>    
</svg>
<div class="chain"></div>

所以,sn-p 的下半部分,你可以看到我是如何通过使用关键帧生成“旋转链效果”的。


我想要什么

我的总体愿望是生成如下内容:

想想传送带的横截面,以及“末端的齿轮如何驱动传送带”。我正在尝试重现这一点。 (即虚线边框的金块应该在齿轮的凹槽内,并被它“拉动”)

#one{
  -webkit-animation: rotateClockwiseAnimation 5s linear infinite; /* Safari 4+ */
  -moz-animation:    rotateClockwiseAnimation 5s linear infinite; /* Fx 5+ */
  -o-animation:      rotateClockwiseAnimation 5s linear infinite; /* Opera 12+ */
  animation:         rotateClockwiseAnimation 5s linear infinite; /* IE 10+, Fx 29+ */
border:5px dashed gold;
  border-radius:50%;
}

@-webkit-keyframes rotateClockwiseAnimation {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
@-moz-keyframes rotateClockwiseAnimation{
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
@-o-keyframes rotateClockwiseAnimation {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
@keyframes rotateClockwiseAnimation {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
<svg id="one" style="width:50px" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 100">
    <defs>
        <circle id="c" cx="50" cy="50" r="30" stroke="#808080" fill="none" stroke-width="25"/>
        <path id="d" stroke="#808080" stroke-width="16" d="M50 0, V15 M50 100, V85 M0 50, H15 M100 50, H85"/>
    </defs>    
    <use xlink:href="#c"/>
    <use xlink:href="#d"/>
    <use xlink:href="#d" transform="rotate(45, 50, 50)"/>
</svg>

但金色的破折号适合齿轮的凹槽,以及屏幕宽度的 80%(如果有意义的话)。

最后,我想生成如下图所示的内容:

看看我希望链条如何“旋转”?


我目前的问题

  • 由于动画是通过使用伪元素“破解”的,我发现实际上很难同步这个“链”的旋转。
  • 我还在学习关键帧动画,所以我确信这不是最好的方法
  • 同样,svg 对我来说是一个新概念,所以请忍受我不愿意使用它(因此我将 css 用于“链”)
  • 最后,我想“让它看起来像”齿轮正在转动链条,但现在它们看起来像是完全(而且做得很糟糕)独立的元素动画

【问题讨论】:

标签: css svg css-animations css-shapes


【解决方案1】:

使用画布

形状(齿轮和链条)和行军蚂蚁动画效果(虚线边框)也可以通过使用 Canvas 绘图来实现。 Canvas 的浏览器支持是quite good

虽然 Canvas 具有基于光栅的缺点(与基于形状的 SVG 相对),但只要不过度缩放画布,这不是一个大问题。在处理大量对象和实时动画时,Canvas 预计会更好。 Here 是 MSDN 上一篇关于何时使用 Canvas 或 SVG 的有趣文章。


形状的构造

以下是本动画中涉及的关键部分/形状:

  • 左齿轮
  • 右齿轮
  • 顶部齿轮

:链是通过绘制两条水平线(使用lineTo 命令)创建的,它们的两端由半圆连接(使用arc 命令绘制)。虚线边框效果是通过使用setLineDash方法来实现的。 setLineDash 方法有两个参数,第一个代表破折号的长度,第二个代表破折号之间的间距。

下面的 sn-p 显示了创建链所需的最少代码:

window.onload = function() {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');
  var chain = {
    offset: 0,
    paint: function() {
      ctx.beginPath();
      ctx.moveTo(75, 50);
      ctx.lineTo(533, 50);
      ctx.arc(533, 100, 50, (Math.PI * 1.5), (Math.PI * 0.5), false);
      ctx.lineTo(75, 150);
      ctx.arc(75, 100, 50, (Math.PI * 0.5), (Math.PI * 1.5), false);
      ctx.lineWidth = 5;
      ctx.fillStyle = 'transparent';
      ctx.setLineDash([12, 14.16]);
      ctx.lineDashOffset = this.offset;
      ctx.fill();
      ctx.stroke();
    }
  };

  chain.paint();
}
/* CSS needed only for demo */

body {
  background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
canvas {
  margin: 50px auto;
}
&lt;canvas id='canvas' width='650' height='300'&gt;&lt;/canvas&gt;

齿轮:所有三个齿轮(左、右和顶部)都是使用相同的命令实现的,唯一的区别是它们在屏幕上的位置(以及顶部的半径)。

齿轮的辐条或齿是使用arc 命令创建的(与链条的半圆一样),并使用相同的半径。他们的lineDashoffset 进行了调整,以便它们占据链的破折号之间留下的确切空间。

齿轮的主体由两个圆组成,外圆的半径大于内圆。 evenodd 填充参数用于设置背景颜色(本例中为tan),仅在外圆上设置,而使内圆透明。

下面的 sn-p 显示了创建 cogs 所需的最少代码:

window.onload = function() {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');
  var cog = {
    paint: function(x, y, r, offset) {
      ctx.beginPath();
      ctx.arc(x, y, r, 0, Math.PI * 2, true);
      ctx.closePath();
      ctx.lineWidth = 5;
      ctx.setLineDash([12, 14.16]);
      ctx.lineDashOffset = offset;
      ctx.strokeStyle = 'tan';
      ctx.stroke();
      ctx.beginPath();
      ctx.arc(x, y, (r - 2), 0, Math.PI * 2, true);
      ctx.arc(x, y, (r / 2.5), 0, Math.PI * 2, true);
      ctx.closePath();
      ctx.fillStyle = 'tan';
      ctx.fill('evenodd');
    }
  };

  function paint() {
    var cog_radius = 50;
    var cog_t_x = 30,
      cog_t_y = 40,
      cog_t_offset = 20.5,
      cog_l_x = 75,
      cog_l_y = 100,
      cog_l_offset = 24.25,
      cog_r_x = 533,
      cog_r_offset = 11.25;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.save();
    cog.paint(cog_t_x, cog_t_y, (cog_radius / 2), cog_t_offset);
    ctx.restore();
    ctx.save();
    cog.paint(cog_l_x, cog_l_y, cog_radius, cog_l_offset);
    ctx.restore();
    ctx.save();
    cog.paint(cog_r_x, cog_l_y, cog_radius, cog_r_offset);
    ctx.restore();
  }
  paint();

}
/* CSS needed only for demo */

body {
  background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
canvas {
  margin: 50px auto;
}
&lt;canvas id='canvas' width='650' height='300'&gt;&lt;/canvas&gt;

动画

动画是通过移动动画每一帧中的笔画lineDashOffset来实现的。动画本身是使用window.requestAnimationFrame 方法触发的,该方法定期调用绘制函数(作为参数传递)。回调速率一般为每秒 60 次(引用 MDN)。在画布的每次重绘期间移动偏移量会使其呈现动画效果。

可以通过调用cancelAnimationFrame 方法在任何时间点停止动画。这可以基于某些用户交互(如点击、悬停等)或基于超时来完成。

window.onload = function() {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');
  var anim, onState = false,
    counter = 0;
  var chain = {
    offset: 0,
    paint: function() {
      ctx.beginPath();
      ctx.moveTo(75, 50);
      ctx.lineTo(533, 50);
      ctx.arc(533, 100, 50, (Math.PI * 1.5), (Math.PI * 0.5), false);
      ctx.lineTo(75, 150);
      ctx.arc(75, 100, 50, (Math.PI * 0.5), (Math.PI * 1.5), false);
      ctx.lineWidth = 5;
      ctx.fillStyle = 'transparent';
      ctx.setLineDash([12, 14.16]);
      ctx.lineDashOffset = this.offset;
      ctx.fill();
      ctx.stroke();
    }
  };

  function paint(type) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    chain.offset += (6.54 / 10);
    ctx.save();
    chain.paint();
    ctx.restore();
    if (type) {
      anim = window.requestAnimationFrame(function() {
        paint(type);
      })
    }
  }
  paint(true);
}
/* CSS needed only for demo */

body {
  background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
canvas {
  margin: 50px auto;
}
&lt;canvas id='canvas' width='650' height='300'&gt;&lt;/canvas&gt;

全图

将所有部分放在一起,下面的 sn-p 提供了链条和齿轮的完整图片以及动画:

window.onload = function() {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');
  var anim, onState = false,
    counter = 0;
  var cog_radius = 50;
  var cog_t_x = 30,
    cog_t_y = 40,
    cog_t_offset = 20.5,
    cog_l_x = 75,
    cog_l_y = 100,
    cog_l_offset = 24.25,
    cog_r_x = 533,
    cog_r_offset = 11.25;
  var chain = {
    offset: 0,
    paint: function() {
      ctx.beginPath();
      ctx.moveTo(75, 50);
      ctx.lineTo(533, 50);
      ctx.arc(533, 100, 50, (Math.PI * 1.5), (Math.PI * 0.5), false);
      ctx.lineTo(75, 150);
      ctx.arc(75, 100, 50, (Math.PI * 0.5), (Math.PI * 1.5), false);
      ctx.lineWidth = 5;
      ctx.fillStyle = 'transparent';
      ctx.setLineDash([12, 14.16]);
      ctx.lineDashOffset = this.offset;
      ctx.fill();
      ctx.stroke();
    }
  };
  var cog = {
    paint: function(x, y, r, offset) {
      ctx.beginPath();
      ctx.arc(x, y, r, 0, Math.PI * 2, true);
      ctx.closePath();
      ctx.lineWidth = 5;
      ctx.setLineDash([12, 14.16]);
      ctx.lineDashOffset = offset;
      ctx.strokeStyle = 'tan';
      ctx.stroke();
      ctx.beginPath();
      ctx.arc(x, y, (r - 2), 0, Math.PI * 2, true);
      ctx.arc(x, y, (r / 2.5), 0, Math.PI * 2, true);
      ctx.closePath();
      ctx.fillStyle = 'tan';
      ctx.fill('evenodd');
    }
  };

  function paint(type) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    chain.offset += (6.54 / 10);
    cog_l_offset -= (6.54 / 10);
    cog_r_offset -= (6.54 / 10);
    cog_t_offset += (6.54 / 10);
    ctx.save();
    cog.paint(cog_t_x, cog_t_y, (cog_radius / 2), cog_t_offset);
    ctx.restore();
    ctx.save();
    chain.paint();
    ctx.restore();
    ctx.save();
    cog.paint(cog_l_x, cog_l_y, cog_radius, cog_l_offset);
    ctx.restore();
    ctx.save();
    cog.paint(cog_r_x, cog_l_y, cog_radius, cog_r_offset);
    ctx.restore();
    if (type) {
      anim = window.requestAnimationFrame(function() {
        paint(type);
      })
    }
  }
  paint(true);
}
/* CSS needed only for demo */

body {
  background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
canvas {
  margin: 50px auto;
}
&lt;canvas id='canvas' width='650' height='300'&gt;&lt;/canvas&gt;

用户互动

如前所述,还可以使用事件侦听器 (addEventListener) 添加用户交互。如果操作需要由用户对画布的特定形状或部分的操作触发,则可以使用pointInPath 方法来验证该点是否在画布的所需部分内。

Here 是 CodePen 演示的链接,该演示还添加了这些用户交互(以及一些其他额外内容)。

注意:有关如何添加用户交互等的说明超出了此特定答案的范围。但是,如果您需要任何帮助,可以通过this chat room 找到我。

【讨论】:

    【解决方案2】:

    这里是使用 CSS 实现 cog 动画的另一种方法。此方法已在 IE11、IE10、Firefox、Chrome、Opera 和 Safari 中测试。

    • 用于齿轮/嵌齿轮的两个圆形元件,带有 box-shadow 以产生内圆。牙齿由围绕轴旋转的子元素(正常 + 伪)生成。
    • 皮带的弯曲部分是使用与齿轮辐条相同的技术实现的,其位置始终位于齿之间。
    • 一个矩形容器元素,其顶部和底部边界使用线性渐变来模拟。这个元素的背景(除了顶部和底部的渐变)是纯色,这是一个缺点。这种纯色用于隐藏两侧圆形元素的一半。
    • 动画通过两种方式实现:(a) 不断旋转圆形元素,(b) 不断改变渐变背景的背景位置。

    .chain {
        margin: 45px auto;
        height: 100px;
        width: 310px;
        position: relative;
        background: -webkit-linear-gradient(0deg, gold 50%, transparent 50%), -webkit-linear-gradient(0deg, gold 50%, transparent 50%), white;
        background: -moz-linear-gradient(90deg, gold 50%, transparent 50%), -moz-linear-gradient(90deg, gold 50%, transparent 50%), white;
        background: linear-gradient(90deg, gold 50%, transparent 50%), linear-gradient(90deg, gold 50%, transparent 50%), white;
        background-size: 41px 5px;
        background-repeat: repeat-x;
        background-position: 20px 0px, 20px 95px;
        -webkit-animation: bgPos 1s infinite linear;
        -moz-animation: bgPos 1s infinite linear;
        animation: bgPos 1s infinite linear;
    }
    .belt, .belt-after, .belt .spokes, .belt .spokes:before, .belt .spokes:after, .belt-after .spokes, .belt-after .spokes:before, .belt-after .spokes:after {
        position: absolute;
        content:'';
        height: 90px;
        width:15px;
        top: 0px;
        border-top: 5px solid gold;
        border-bottom: 5px solid gold;
        z-index: -1;
    }
    .belt, .belt-after {
        -webkit-animation: borderAnim 8s infinite linear;
        -moz-animation: borderAnim 8s infinite linear;
        animation: borderAnim 8s infinite linear;
    }
    .belt .spokes, .belt-after .spokes {
        top: -5px;
        -webkit-transform: rotate(45deg);
        -moz-transform: rotate(45deg);
        transform: rotate(45deg);
    }
    .belt .spokes:before, .belt-after .spokes:before {
        top: -5px;
        -webkit-transform: rotate(90deg);
        -moz-transform: rotate(90deg);
        transform: rotate(90deg);
    }
    .belt .spokes:after, .belt-after .spokes:after {
        top: -5px;
        -webkit-transform: rotate(45deg);
        -moz-transform: rotate(45deg);
        transform: rotate(45deg);
    }
    .belt {
        left: -16px;
    }
    .belt-after {
        right: -16px;
    }
    .gear {
        content:'';
        position: absolute;
        top: 5px;
        height: 90px;
        width: 90px;
        border-radius: 50%;
        -webkit-animation: borderAnim 8s infinite linear;
        -moz-animation: borderAnim 8s infinite linear;
        animation: borderAnim 8s infinite linear;
        box-shadow: inset 0px 0px 0px 30px gray;
        z-index: 4;
    }
    .gear:before, .gear .spokes, .gear .spokes:before, .gear .spokes:after {
        position: absolute;
        content:'';
        height: 88px;
        width:15px;
        top: -5px;
        border-top: 6px solid gray;
        border-bottom: 6px solid gray;
    }
    .gear:before {
        left: 37px;
        -webkit-transform: rotate(22.5deg);
        -moz-transform: rotate(22.5deg);
        transform: rotate(22.5deg);
    }
    .gear .spokes {
        left: 37px;
        -webkit-transform: rotate(67.5deg);
        -moz-transform: rotate(67.5deg);
        transform: rotate(67.5deg);
    }
    .gear .spokes:before {
        top: -6px;
        -webkit-transform: rotate(45deg);
        -moz-transform: rotate(45deg);
        transform: rotate(45deg);
    }
    .gear .spokes:after {
        -webkit-transform: rotate(90deg);
        -moz-transform: rotate(90deg);
        transform: rotate(90deg);
    }
    .chain .belt + .gear {
        left:-52px;
    }
    .chain .belt-after + .gear {
        right: -52.5px;
    }
    .gear-small {
        content:'';
        position: absolute;
        left: -92px;
        top: -20px;
        height: 50px;
        width: 50px;
        border-radius: 50%;
        -webkit-animation: borderAnim 6s infinite linear reverse;
        -moz-animation: borderAnim 6s infinite linear reverse;
        animation: borderAnim 6s infinite linear reverse;
        box-shadow: inset 0px 0px 0px 20px gray;
        z-index: -2;
    }
    .gear-small:before {
        position: absolute;
        content:'';
        left: 21px;
        top: -3px;
        height: 48px;
        width: 10px;
        border-top:4px solid gray;
        border-bottom: 4px solid gray;
    }
    .gear-small .spokes, .gear-small .spokes:before, .gear-small .spokes:after {
        position: absolute;
        content:'';
        left: 21px;
        top: -3px;
        height: 48px;
        width: 10px;
        border-top:4px solid gray;
        border-bottom: 4px solid gray;
    }
    .gear-small .spokes {
        -webkit-transform: rotate(45deg);
        -moz-transform: rotate(45deg);
        transform: rotate(45deg);
    }
    .gear-small .spokes:before {
        left: 0px;
        -webkit-transform: rotate(90deg);
        -moz-transform: rotate(90deg);
        transform: rotate(90deg);
    }
    .gear-small .spokes:after {
        left: 0px;
        -webkit-transform: rotate(45deg);
        -moz-transform: rotate(45deg);
        transform: rotate(45deg);
    }
    @-webkit-keyframes borderAnim {
        0% {
            -webkit-transform: rotate(360deg);
        }
        100% {
            -webkit-transform: rotate(0deg);
        }
    }
    @-moz-keyframes borderAnim {
        0% {
            -moz-transform: rotate(360deg);
        }
        100% {
            -moz-transform: rotate(0deg);
        }
    }
    @keyframes borderAnim {
        0% {
            transform: rotate(360deg);
        }
        100% {
            transform: rotate(0deg);
        }
    }
    @-webkit-keyframes bgPos {
        0% {
            background-position: 20px 0px, -20px 95px;
        }
        100% {
            background-position: -20px 0px, 20px 95px;
        }
    }
    @-moz-keyframes bgPos {
        0% {
            background-position: 20px 0px, -20px 95px;
        }
        100% {
            background-position: -20px 0px, 20px 95px;
        }
    }
    @keyframes bgPos {
        0% {
            background-position: 20px 0px, -20px 95px;
        }
        100% {
            background-position: -20px 0px, 20px 95px;
        }
    }
    <div class="chain">
        <div class="gear-small">
            <div class="spokes"></div>
        </div>
        <div class="belt">
            <div class="spokes"></div>
        </div>
        <div class="gear">
            <div class="spokes"></div>
        </div>
        <div class="belt-after">
            <div class="spokes"></div>
        </div>
        <div class="gear">
            <div class="spokes"></div>
        </div>
    </div>

    奖励:这是带有开/关开关的整个动画:) 单击(拉动)链条手柄以打开或关闭动画。

    .container {
      position: relative;
    }
    .chain {
      margin: 45px 100px;
      height: 100px;
      width: 310px;
      position: relative;
      background: -webkit-linear-gradient(0deg, gold 50%, transparent 50%), -webkit-linear-gradient(0deg, gold 50%, transparent 50%), white;
      background: -moz-linear-gradient(90deg, gold 50%, transparent 50%), -moz-linear-gradient(90deg, gold 50%, transparent 50%), white;
      background: linear-gradient(90deg, gold 50%, transparent 50%), linear-gradient(90deg, gold 50%, transparent 50%), white;
      background-size: 41px 5px;
      background-repeat: repeat-x;
      background-position: 20px 0px, 20px 95px;
      -webkit-animation: bgPos 1s infinite linear;
      -moz-animation: bgPos 1s infinite linear;
      animation: bgPos 1s infinite linear;
    }
    .belt,
    .belt-after,
    .belt .spokes,
    .belt .spokes:before,
    .belt .spokes:after,
    .belt-after .spokes,
    .belt-after .spokes:before,
    .belt-after .spokes:after {
      position: absolute;
      height: 90px;
      width: 15px;
      top: 0px;
      border-top: 5px solid gold;
      border-bottom: 5px solid gold;
      z-index: -1;
    }
    .belt,
    .belt-after {
      -webkit-animation: borderAnim 8s infinite linear;
      -moz-animation: borderAnim 8s infinite linear;
      animation: borderAnim 8s infinite linear;
    }
    .belt .spokes,
    .belt-after .spokes {
      top: -5px;
      -webkit-transform: rotate(45deg);
      -moz-transform: rotate(45deg);
      transform: rotate(45deg);
    }
    .belt .spokes:before,
    .belt .spokes:after,
    .belt-after .spokes,
    .belt-after .spokes:before,
    .belt-after .spokes:after {
      content: '';
    }
    .belt .spokes:before,
    .belt-after .spokes:before {
      top: -5px;
      -webkit-transform: rotate(90deg);
      -moz-transform: rotate(90deg);
      transform: rotate(90deg);
    }
    .belt .spokes:after,
    .belt-after .spokes:after {
      top: -5px;
      -webkit-transform: rotate(45deg);
      -moz-transform: rotate(45deg);
      transform: rotate(45deg);
    }
    .belt {
      left: -16px;
    }
    .belt-after {
      right: -16px;
    }
    .gear {
      position: absolute;
      top: 5px;
      height: 90px;
      width: 90px;
      border-radius: 100%;
      -webkit-animation: borderAnim 8s infinite linear;
      -moz-animation: borderAnim 8s infinite linear;
      animation: borderAnim 8s infinite linear;
      box-shadow: inset 0px 0px 0px 30px gray, inset 0px 0px 0px 40px white, inset 0px 0px 0px 50px tomato;
      z-index: 4;
    }
    .gear:before,
    .gear .spokes,
    .gear .spokes:before,
    .gear .spokes:after {
      position: absolute;
      content: '';
      height: 88px;
      width: 15px;
      top: -5px;
      border-top: 6px solid gray;
      border-bottom: 6px solid gray;
    }
    .gear:before {
      left: 37px;
      -webkit-transform: rotate(22.5deg);
      -moz-transform: rotate(22.5deg);
      transform: rotate(22.5deg);
    }
    .gear .spokes {
      left: 37px;
      -webkit-transform: rotate(67.5deg);
      -moz-transform: rotate(67.5deg);
      transform: rotate(67.5deg);
    }
    .gear .spokes:before {
      top: -6px;
      -webkit-transform: rotate(45deg);
      -moz-transform: rotate(45deg);
      transform: rotate(45deg);
    }
    .gear .spokes:after {
      -webkit-transform: rotate(90deg);
      -moz-transform: rotate(90deg);
      transform: rotate(90deg);
    }
    .chain .belt + .gear {
      left: -52px;
    }
    .chain .belt-after + .gear {
      right: -52.5px;
    }
    .gear-small {
      position: absolute;
      left: -91px;
      top: -20px;
      height: 50px;
      width: 50px;
      border-radius: 50%;
      -webkit-animation: borderAnim 8s 0.4s infinite linear;
      -moz-animation: borderAnim 6s infinite linear;
      animation: borderAnim 6s infinite linear;
      -webkit-animation-direction: reverse;
      -moz-animation-direction: reverse;
      animation-direction: reverse;
      box-shadow: inset 0px 0px 0px 20px gray;
      z-index: -2;
    }
    .gear-small:before {
      position: absolute;
      content: '';
      left: 21px;
      top: -3px;
      height: 48px;
      width: 10px;
      border-top: 4px solid gray;
      border-bottom: 4px solid gray;
    }
    .gear-small .spokes,
    .gear-small .spokes:before,
    .gear-small .spokes:after {
      position: absolute;
      content: '';
      left: 21px;
      top: -3px;
      height: 48px;
      width: 10px;
      border-top: 4px solid gray;
      border-bottom: 4px solid gray;
    }
    .gear-small .spokes {
      -webkit-transform: rotate(45deg);
      -moz-transform: rotate(45deg);
      transform: rotate(45deg);
    }
    .gear-small .spokes:before {
      left: 0px;
      -webkit-transform: rotate(90deg);
      -moz-transform: rotate(90deg);
      transform: rotate(90deg);
    }
    .gear-small .spokes:after {
      left: 0px;
      -webkit-transform: rotate(45deg);
      -moz-transform: rotate(45deg);
      transform: rotate(45deg);
    }
    @-webkit-keyframes borderAnim {
      0% {
        -webkit-transform: rotate(360deg);
      }
      100% {
        -webkit-transform: rotate(0deg);
      }
    }
    @-moz-keyframes borderAnim {
      0% {
        -moz-transform: rotate(360deg);
      }
      100% {
        -moz-transform: rotate(0deg);
      }
    }
    @keyframes borderAnim {
      0% {
        transform: rotate(360deg);
      }
      100% {
        transform: rotate(0deg);
      }
    }
    @-webkit-keyframes bgPos {
      0% {
        background-position: 20px 0px, -20px 95px;
      }
      100% {
        background-position: -20px 0px, 20px 95px;
      }
    }
    @-moz-keyframes bgPos {
      0% {
        background-position: 20px 0px, -20px 95px;
      }
      100% {
        background-position: -20px 0px, 20px 95px;
      }
    }
    @keyframes bgPos {
      0% {
        background-position: 20px 0px, -20px 95px;
      }
      100% {
        background-position: -20px 0px, 20px 95px;
      }
    }
    #pull-switch {
      display: none;
      /* no need to display checkbox */
    }
    #switch {
      position: absolute;
      left: 77px;
      top: 50px;
      border-right: 2px dotted tomato;
      height: 200px;
      width: 15px;
      -webkit-transition: height 0.5s;
      -moz-transition: height 0.5s;
      transition: height 0.5s;
      z-index: 10;
    }
    #handle {
      display: block;
      position: absolute;
      /* left: width of chain div (15px) + half of border (1px) - radius of handle (8px)*/
      left: 8px;
      bottom: 0%;
      background-color: tomato;
      width: 16px;
      height: 16px;
      border-radius: 50%;
      cursor: pointer
    }
    #pull-switch:checked + #switch > #handle {
      background-color: seagreen;
    }
    #pull-switch:checked + #switch {
      height: 225px;
      border-color: seagreen;
    }
    #pull-switch ~ .chain .gear-small,
    #pull-switch ~ .chain .belt,
    #pull-switch ~ .chain .belt-after,
    #pull-switch ~ .chain .gear,
    #pull-switch ~ .chain {
      -webkit-animation-play-state: paused;
      -moz-animation-play-state: paused;
      animation-play-state: paused;
    }
    #pull-switch:checked ~ .chain .gear-small,
    #pull-switch:checked ~ .chain .belt,
    #pull-switch:checked ~ .chain .belt-after,
    #pull-switch:checked ~ .chain .gear,
    #pull-switch:checked ~ .chain {
      -webkit-animation-play-state: running;
      -moz-animation-play-state: running;
      animation-play-state: running;
    }
    #pull-switch:checked ~ .chain .belt + .gear,
    #pull-switch:checked ~ .chain .belt-after + .gear {
      box-shadow: inset 0px 0px 0px 30px gray, inset 0px 0px 0px 40px white, inset 0px 0px 0px 50px seagreen;
    }
    <div class="container">
      <input type="checkbox" id="pull-switch" />
      <div id="switch">
        <label for="pull-switch" id="handle"></label>
      </div>
      <div class="chain">
        <div class="gear-small">
          <div class="spokes"></div>
        </div>
        <div class="belt">
          <div class="spokes"></div>
        </div>
        <div class="gear">
          <div class="spokes"></div>
        </div>
        <div class="belt-after">
          <div class="spokes"></div>
        </div>
        <div class="gear">
          <div class="spokes"></div>
        </div>
      </div>
    </div>

    原答案:(由于虚线边框错误而在 Firefox 上不起作用,并且 IE 中的虚线更接近,使其看起来很难看)。

    您可以通过以下组合来实现边框旋转动画:

    • 两个圆形元素(使用border-radius: 50%),两边都有虚线边框,形成边框的弯曲部分。
    • 一个矩形容器元素,其顶部和底部边框使用linear-gradient 模拟。这个元素的背景(除了顶部和底部的渐变)是纯色,这是一个缺点。这种纯色用于隐藏两侧圆形元素的一半。
    • 动画是通过两种方式实现的:(a) 不断旋转两个圆形元素;(b) 不断改变渐变背景的background-position
    • 齿轮也是圆形元素,其中辐条由虚线边框制成,实心内部部分使用插图box-shadow 生成。齿轮的旋转方式使链条的边界始终位于齿轮的边界之间。

    .chain {
      margin: 45px auto;
      height: 100px;
      width: 300px;
      position: relative;
      background: -webkit-linear-gradient(0deg, gold 50%, transparent 50%), -webkit-linear-gradient(0deg, gold 50%, transparent 50%), white;
      background: -moz-linear-gradient(90deg, gold 50%, transparent 50%), -moz-linear-gradient(90deg, gold 50%, transparent 50%), white;
      background: linear-gradient(90deg, gold 50%, transparent 50%), linear-gradient(90deg, gold 50%, transparent 50%), white;
      background-size: 30px 5px;
      background-repeat: repeat-x;
      background-position: 0px 0px, 5px 95px;
      -webkit-animation: bgPos 4s infinite linear;
      -moz-animation: bgPos 4s infinite linear;
      animation: bgPos 4s infinite linear;
    }
    .chain .before,
    .chain .after {
      position: absolute;
      content: '';
      height: 90px;
      width: 90px;
      top: 0px;
      border-radius: 50%;
      border: 5px dashed gold;
      -webkit-animation: borderAnim 2s infinite linear;
      -moz-animation: borderAnim 2s infinite linear;
      animation: borderAnim 2s infinite linear;
      z-index: -2;
    }
    .chain .before {
      left: -45px;
    }
    .chain .after {
      right: -45px;
    }
    .chain .gear {
      content: '';
      position: absolute;
      top: 0px;
      height: 90px;
      width: 90px;
      border-radius: 50%;
      border: 5px dashed gray;
      -webkit-transform: rotate(16deg);
      -moz-transform: rotate(16deg);
      transform: rotate(16deg);
      -webkit-animation: gearAnim 2s infinite linear;
      -moz-animation: gearAnim 2s infinite linear;
      animation: gearAnim 2s infinite linear;
      box-shadow: inset 0px 0px 0px 30px gray;
      z-index: 4;
    }
    .chain .before + .gear {
      left: -45px;
    }
    .chain .after + .gear {
      right: -45px;
    }
    .gear-small {
      content: '';
      position: absolute;
      left: -95px;
      top: -23px;
      height: 60px;
      width: 60px;
      border-radius: 50%;
      border: 3px dashed gray;
      -webkit-transform: rotate(16deg);
      -moz-transform: rotate(16deg);
      transform: rotate(16deg);
      -webkit-animation: gearAnim 6s infinite linear reverse;
      -moz-animation: gearAnim 6s infinite linear reverse;
      animation: gearAnim 6s infinite linear reverse;
      box-shadow: inset 0px 0px 0px 20px gray;
      z-index: -2;
    }
    @-webkit-keyframes borderAnim {
      0% {
        -webkit-transform: rotate(360deg);
      }
      100% {
        -webkit-transform: rotate(0deg);
      }
    }
    @-moz-keyframes borderAnim {
      0% {
        -moz-transform: rotate(360deg);
      }
      100% {
        -moz-transform: rotate(0deg);
      }
    }
    @keyframes borderAnim {
      0% {
        transform: rotate(360deg);
      }
      100% {
        transform: rotate(0deg);
      }
    }
    @-webkit-keyframes bgPos {
      0% {
        background-position: 610px 0px, 0px 95px;
      }
      100% {
        background-position: 0px 0px, 600px 95px;
      }
    }
    @-moz-keyframes bgPos {
      0% {
        background-position: 610px 0px, 0px 95px;
      }
      100% {
        background-position: 0px 0px, 600px 95px;
      }
    }
    @keyframes bgPos {
      0% {
        background-position: 610px 0px, 0px 95px;
      }
      100% {
        background-position: 0px 0px, 600px 95px;
      }
    }
    @-webkit-keyframes gearAnim {
      0% {
        -webkit-transform: rotate(376deg);
      }
      100% {
        -webkit-transform: rotate(16deg);
      }
    }
    @-moz-keyframes gearrAnim {
      0% {
        -moz-transform: rotate(376deg);
      }
      100% {
        -moz-transform: rotate(16deg);
      }
    }
    @keyframes gearAnim {
      0% {
        transform: rotate(376deg);
      }
      100% {
        transform: rotate(16deg);
      }
    }
    <div class="chain">
      <div class="gear-small"></div>
      <div class="before"></div>
      <div class="gear"></div>
      <div class="after"></div>
      <div class="gear"></div>
    </div>

    最后但同样重要的是,我仍然建议为此使用 SVG 方法,因为超过某个点,使用 CSS 的此类动画会变得非常混乱 :)

    【讨论】:

    • 嘿,它很好,但不适用于 Chromium V.38.0.2125.111
    • @ThePragmatick:是的,线性渐变线中的-webkit- 前缀错误,由于角度不同,因此导致了问题。现在解决了:)
    • 不错!现在它的工作。但正如您所说,SVG 是这里的最佳选择。虚线边框变得杂乱无章。
    • @JeanGkol:不是问题,伙计。我无法检查它,因为我使用的是旧的 FF 版本并且它确实存在问题(圆形位看起来很坚固)。将在答案中添加注释以帮助未来的读者。
    • 我在 chrome 中看到了左右边框,它确实有效:D
    【解决方案3】:

    注意:我已经在 box-shadow 中重新制作了整个动画,因为使用虚线边框并不能在所有浏览器上获得一致的输出。

    工作

    .. 和 Works 跨浏览器。
    FF 5+、GC 4+、IE9+、Safari 4+、Opera 12.1+

    你可以用 box-shadow 试试这个:

    • 要制作齿轮齿,请使用具有负扩展半径的 box-shadow。例如,我的装备尺寸是50px,所以为了将box-shadowd=8px 相结合,我使用-46px 作为传播半径。

    • 我使用坐标geo定位牙齿,只用了8颗牙齿来简化。

    • 现在对于直线输送机,我们需要知道齿之间的距离。我们通过以下方式得到:

    • 2*pi*(gear radius) / no. of teeth = (pi * r) / 4
      我的 = (55 * 3.1415) / 4 = 43(大约)
      我将半径设为 55,因为齿的半径为 4px,并且距离齿轮圆周 1px。

    • 为了对齐顶部和底部的直线传送带,它们需要平移距离的任意倍数。对于我的装备,我将它们平移 43 像素。

    脚手架

    FIDDLE

    body {
        background: rgba(25,80,175, 0.4);
    }
    .rect {
        height: 116px;
        width: 401px;
        border-radius: 58px;
        position: relative;
        overflow: hidden;
    }
    
    .rect:before, .rect:after {
        content: '';
        position: absolute;
        left: 46px; /*50-half width*/
        height: 8px;
        width: 8px;
        border-radius: 50%;
        background: transparent;
        box-shadow: 43px 0 0 0 white, 86px 0 0 0 white, 129px 0 0 0 white, 172px 0 0 0 white, 215px 0 0 0 white, 258px 0 0 0 white, 301px 0 0 0 white;
        -webkit-animation: apple 0.3s linear infinite;
        -moz-animation: apple 0.3s linear infinite;
        animation: apple 0.3s linear infinite;
    }
    .rect:before {
        top: 0px;
    }
    .rect:after {
        bottom: 0px;
        -webkit-animation-direction: reverse;
        -moz-animation-direction: reverse;
        animation-direction: reverse;
    }
    @-webkit-keyframes apple {
        0% {-webkit-transform: translatex(0px);}
        100% {-webkit-transform: translateX(-43px);}
    }
    @-moz-keyframes apple {
        0% {-moz-transform: translatex(0px);}
        100% {-moz-transform: translateX(-43px);}
    }
    @keyframes apple {
        0% {transform: translatex(0px);}
        100% {transform: translateX(-43px);}
    }
    .left, .right {
        content: '';
        position: relative;
        height: 100px;
        width: 100px;
        border-radius: 50px;
        background-color: #222;
        box-shadow: 0 55px 0 -46px white, 55px 0 0 -46px white, 0 -55px 0 -46px white, -55px 0 0 -46px white,
            39px 39px 0 -46px white, -39px -39px 0 -46px white, 39px -39px 0 -46px white, -39px 39px 0 -46px white;
        -webkit-animation: mango 2.4s linear infinite;
        -moz-animation: mango 2.4s linear infinite;
        animation: mango 2.4s linear infinite;
    }
    .left {
        top: -108px;
        left: 0px;
    }
    .right {
        top: -208px;
        left: 301px;
    }
    @-webkit-keyframes mango {
        0% {-webkit-transform: rotate(0deg);}
        100% {-webkit-transform: rotate(-360deg);}
    }
    @-moz-keyframes mango {
        0% {-moz-transform: rotate(0deg);}
        100% {-moz-transform: rotate(-360deg);}
    }
    @keyframes mango {
        0% {transform: rotate(0deg);}
        100% {transform: rotate(-360deg);}
    }
    <div class="rect"></div>
    <div class="left"></div>
    <div class="right"></div>

    最终版

    ...带齿轮。链条当前是dotted虚线!

    FIDDLE

    body {
        background-color: white;
    }
    .rect {
        height: 120px;
        width: 401px;
        border-radius: 58px;
        position: relative;
    }
    
    .rect:before, .rect:after {
        content: '';
        position: absolute;
        left: 40px; /*50-half width*/
        height: 10px;
        width: 20px;
        background: transparent;
        box-shadow: 43px 0 0 0 gold, 86px 0 0 0 gold, 129px 0 0 0 gold, 172px 0 0 0 gold, 215px 0 0 0 gold, 258px 0 0 0 gold, 301px 0 0 0 gold, 344px 0 0 0 gold; /*keep adding 43 to x-axis*/
        -webkit-animation: apple 0.6s linear infinite;
        -moz-animation: apple 0.6s linear infinite;
        animation: apple 0.6s linear infinite;
        overflow: hidden;
    }
    .rect:before {
        top: 0px;
    }
    .rect:after {
        bottom: 0px;
        -webkit-animation-direction: reverse;
        -moz-animation-direction: reverse;
        animation-direction: reverse;
    }
    @-webkit-keyframes apple {
        0% {-webkit-transform: translatex(0px);}
        100% {-webkit-transform: translateX(-43px);}
    }
    @-moz-keyframes apple {
        0% {-moz-transform: translatex(0px);}
        100% {-moz-transform: translateX(-43px);}
    }
    @keyframes apple {
        0% {transform: translatex(0px);}
        100% {transform: translateX(-43px);}
    }
    .left, .right {
        content: '';
        position: relative;
        height: 100px;
        width: 100px;
        border-radius: 50px;
        -webkit-animation: mango 4.8s linear infinite;
        -moz-animation: mango 4.8s linear infinite;
        animation: mango 4.8s linear infinite;
    }
    .left {
        top: -110px;
        left: 0px;
    }
    .right {
        top: -210px;
        left: 344px;
    }
    .left:before, .left:after, .right:before, .right:after {
        height: 20px;
        width: 20px;
        content: '';
        position: absolute;
        background-color: gold;
    }
    .left:before, .right:before {
        box-shadow: 50px 50px 0 0 gold, -50px 50px 0 0 gold, 0 100px 0 0 gold;
        top: -10px;
        left: 40px;
    }
    .left:after, .right:after {
        transform: rotate(45deg);
        top: 5px;
        left: 76px;
        box-shadow: 0px 100px 0 0 gold, 50px 50px 0 0 gold, -50px 50px 0 0 gold;
    }
    @-webkit-keyframes mango {
        0% {-webkit-transform: rotate(0deg);}
        100% {-webkit-transform: rotate(-360deg);}
    }
    @-moz-keyframes mango {
        0% {-moz-transform: rotate(0deg);}
        100% {-moz-transform: rotate(-360deg);}
    }
    @keyframes mango {
        0% {transform: rotate(0deg);}
        100% {transform: rotate(-360deg);}
    }
    .cover {
        height: 104px;
        width: 446px;
        border-radius: 50px;
        position: relative;
        background: rgba(255,255,255,1);
        top: -312px;
        left; -2px;
        
    }
    .gear, .gear2 {
        height: 100px;
        width: 100px;
        background: dimgray;
        border-radius: 50%;
        position: relative;
        -webkit-animation: gear 4.8s linear infinite;
        -moz-animation: gear 4.8s linear infinite;
        animation: gear 4.8s linear infinite;
    }
    .gear {
        top: -414px;
      
    }
    .gear2 {
        top: -514px;
        left: 345px;
    }
    .gear:before, .gear:after, .gear2:before, .gear2:after {
        height: 20px;
        width: 20px;
        content: '';
        position: absolute;
        background-color: dimgray;
        
    }
    .gear:before, .gear2:before {
        box-shadow: 50px 50px 0 0 dimgray, -50px 50px 0 0 dimgray, 0 100px 0 0 dimgray;
        top: -10px;
        left: 40px;
    }
    .gear:after, .gear2:after {
        transform: rotate(45deg);
        top: 5px;
        left: 76px;
        box-shadow: 0px 100px 0 0 dimgray, 50px 50px 0 0 dimgray, -50px 50px 0 0 dimgray;
    }
    @-webkit-keyframes gear {
        0% {-webkit-transform: rotate(22.5deg);}
        100% {-webkit-transform: rotate(-337.5deg);}
    }
    @-moz-keyframes gear {
        0% {-moz-transform: rotate(22.5deg);}
        100% {-moz-transform: rotate(-337.5deg);}
    }
    @keyframes gear {
        0% {transform: rotate(22.5deg);}
        100% {transform: rotate(-337.5deg);}
    }
    <div class="rect"></div>
    <div class="left"></div>
    <div class="right"></div>
    <div class="cover"></div>
    <div class="gear"></div>
    <div class="gear2"></div>

    最终版本(圆齿)

    .rect {
        height: 120px;
        width: 401px;
        border-radius: 58px;
        position: relative;
    }
    .rect:before, .rect:after {
        content: '';
        position: absolute;
        left: 40px; /*50-half width*/
        height: 10px;
        width: 20px;
        box-shadow: 43px 0 0 0 gold, 86px 0 0 0 gold, 129px 0 0 0 gold, 172px 0 0 0 gold, 215px 0 0 0 gold, 258px 0 0 0 gold, 301px 0 0 0 gold, 344px 0 0 0 gold; /*keep adding 43 to x-axis*/
        -webkit-animation: translate 0.6s linear infinite;
        -moz-animation: translate 0.6s linear infinite;
        animation: translate 0.6s linear infinite;
        overflow: hidden;
    }
    .rect:before {top: 0px;}
    .rect:after {
        bottom: 0px;
        -webkit-animation-direction: reverse;
        -moz-animation-direction: reverse;
        animation-direction: reverse;
    }
    @-webkit-keyframes translate {
        0% {-webkit-transform: translatex(0px);}
        100% {-webkit-transform: translateX(-43px);}
    }
    @-moz-keyframes translate {
        0% {-moz-transform: translatex(0px);}
        100% {-moz-transform: translateX(-43px);}
    }
    @keyframes translate {
        0% {transform: translatex(0px);}
        100% {transform: translateX(-43px);}
    }
    .left, .right {
        position: relative;
        height: 100px;
        width: 100px;
        border-radius: 50px;
        -webkit-animation: rotate 4.8s linear infinite;
        -moz-animation: rotate 4.8s linear infinite;
        animation: rotate 4.8s linear infinite;
    }
    .left {
        top: -110px; left: 0px;
    }
    .right {
        top: -210px; left: 344px;
    }
    .left:before, .left:after, .right:before, .right:after {
        height: 20px;
        width: 20px;
        content: '';
        position: absolute;
        background: gold;
    }
    .left:before, .right:before {
        box-shadow: 50px 50px 0 0 gold, -50px 50px 0 0 gold, 0 100px 0 0 gold;
        top: -10px;
        left: 40px;
    }
    .left:after, .right:after {
        transform: rotate(45deg);
        top: 5px;
        left: 76px;
        box-shadow: 0px 100px 0 0 gold, 50px 50px 0 0 gold, -50px 50px 0 0 gold;
    }
    @-webkit-keyframes rotate {
        0% {-webkit-transform: rotate(0deg);}
        100% {-webkit-transform: rotate(-360deg);}
    }
    @-moz-keyframes rotate {
        0% {-moz-transform: rotate(0deg);}
        100% {-moz-transform: rotate(-360deg);}
    }
    @keyframes rotate {
        0% {transform: rotate(0deg);}
        100% {transform: rotate(-360deg);}
    }
    .cover {
        height: 104px;
        width: 446px;
        border-radius: 50px;
        position: relative;
        background: rgba(255,255,255,1);
        top: -312px;
        left; -2px;
    }
    .gear, .gear2, .gear3 {
        height: 100px;
        width: 100px;
        background: transparent;
        box-shadow: inset 0 0 0px 35px dimgray, inset 0 0 0px 40px #444;
        border-radius: 50%;
        position: relative;
        -webkit-animation: rotate 4.8s linear infinite;
        -moz-animation: rotate 4.8s linear infinite;
        animation: rotate 4.8s linear infinite;
        -webkit-animation-delay: 0.3s;
        -moz-animation-delay: 0.3s;
        animation-delay: 0.3s;
    }
    .gear {top: -414px;}
    .gear2 {top: -514px; left: 345px;}
    .gear:before, .gear:after, .gear2:before, .gear2:after,  .gear3:before, .gear3:after {
        height: 20px;
        width: 20px;
        content: '';
        border-radius: 20%;
        position: absolute;
        background: dimgray;
    }
    .gear:before, .gear2:before, .gear3:before {
        box-shadow: 50px 50px 0 0 dimgray, -50px 50px 0 0 dimgray, 0 100px 0 0 dimgray;
        top: -10px; left: 40px;
    }
    .gear:after, .gear2:after, .gear3:after {
        transform: rotate(45deg);
        top: 5px; left: 76px;
        box-shadow: 0px 100px 0 0 dimgray, 50px 50px 0 0 dimgray, -50px 50px 0 0 dimgray;
    }
    .gear3 {
        -webkit-animation-direction: reverse;
        -moz-animation-direction: reverse;
        animation-direction: reverse;
        top: -504px;
        -webkit-animation-delay: 0s;
        -moz-animation-delay: 0s;
        animation-delay: 0s;
    }
    <div class="rect"></div>
    <div class="left"></div>
    <div class="right"></div>
    <div class="cover"></div>
    <div class="gear"></div>
    <div class="gear2"></div>
    <div class="gear3"></div>

    FIDDLE - ROUNDED TEETH


    注意:要提高动画的速度,您只需按比例减少每个元素的动画持续时间。

    Fiddle (fast)

    【讨论】:

      【解决方案4】:

      齿轮和链条动画:

      我完全重构了代码(CSSHTML),现在是:

      • 更短(尤其是 css)
      • 更简单
      • 更真实:更正了链条和齿轮之间的同步问题,并在右侧添加了一个缺失的齿轮,因为您的链条似乎漂浮在空中:

      DEMO

      方法是相同的,为齿轮设置旋转角度动画,为链条路径设置dash-offset。我调整了两个动画之间的时间,让它看起来好像齿轮在拉动链条。

      浏览器支持:

      作为 IE doesn't support svg animate 元素,我还使用同样支持 IE9 及更高版本的 snap.svg 库制作了这个版本的动画(在 IE9 中使用 crossbrowsertesting 进行了测试)。

      DEMO 支持 IE

      var cont   = new Snap('#svg'),
          chain  = cont.select('#chain'),
          cogAcw = cont.select('#cog_acw'),
          cogCw  = cont.select('#cog_cw'),
          speed  = 500;  // Lower this number to make the animation faster
      
      function infChain(el) {
          var len = el.getTotalLength();
          el.attr({"stroke-dasharray": len/62,"stroke-dashoffset": 0});
          el.animate({"stroke-dashoffset": -len/31}, speed, mina.linear, infChain.bind(null, el));
      }
      function rotateAcw(el) {
          el.transform('r22.5,20,20');
          el.animate({ transform: 'r-22.5,20,20' }, speed, mina.linear, rotateAcw.bind( null, el));
      }
      function rotateCw(el) {
          el.transform('r0,20,20');
          el.animate({ transform: 'r45,20,20' }, speed, mina.linear, rotateCw.bind( null, el));
      }
      infChain(chain);
      rotateAcw(cogAcw);
      rotateCw(cogCw);
      svg {
          width:100%;
      }
      <script src="http://thisisa.simple-url.com/js/snapsvg.js"></script>
      <svg id="svg" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 30">
          <defs>
              <circle id="c" cx="20" cy="20" r="4" stroke="#808080" fill="none" stroke-width="4" />
              <path id="d" stroke="#808080" stroke-width="2" d="M20 13 V16 M27 20 H24 M20 27 V24 M13 20 H16" />
              <g id="cog_acw">
                  <use xlink:href="#c" /><use xlink:href="#d" />
                  <use xlink:href="#d" transform="rotate(45 20 20)" />
              </g>  
              <g id="cog_cw">
                  <use xlink:href="#c" /><use xlink:href="#d" />
                  <use xlink:href="#d" transform="rotate(45 20 20)" />
              </g>  
          </defs>
          <path id="chain" stroke-width="1" stroke="#000" fill="transparent" 
          d="M21.3 13.5 H20 C11.4 13.5 11.4 26.5 20 26.5 H80 C89.4 26.5 89.4 13.5 80.8 13.5z" />
          <use  xlink:href="#cog_acw" />
          <use  transform="translate(60.5 0), rotate(19,20,20)" xlink:href="#cog_acw" />
          <use  transform="translate(-4.5 -4.5),scale(.8), rotate(0,20,20)" xlink:href="#cog_cw" />    
      </svg>

      svg{width:100%;}
      #chain_st{
        -webkit-animation: dash 1s infinite linear;
        -moz-animation: dash 1s infinite linear;
        -o-animation: dash 1s infinite linear;
        animation: dash 1s infinite linear;
      }
      @-webkit-keyframes dash {
        to { stroke-dashoffset: -5; }
      }
      @-moz-keyframes dash {
        to { stroke-dashoffset: -5; }
      }
      @-o-keyframes dash {
        to { stroke-dashoffset: -5; }
      }
      @keyframes dash {
        to { stroke-dashoffset: -5; }
      }
      <svg id="one" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 30">
        <defs>
          <circle id="c" cx="20" cy="20" r="4" stroke="#808080" fill="none" stroke-width="4"/>
          <path id="d" stroke="#808080" stroke-width="2" d="M20 13 V16 M27 20 H24 M20 27 V24 M13 20 H16"/>
          <g id="cog">
            <use xlink:href="#c"/>
            <use xlink:href="#d"/>
            <use xlink:href="#d" transform="rotate(45 20 20)"/>
          </g>
        </defs>
        <g transform="translate(0,-7), scale(0.8), rotate(22.5 8 8)">
          <use xlink:href="#cog">
            <animateTransform attributeType="xml" attributeName="transform" type="rotate" from="-22.5 20 20" to="337.5 20 20" dur="8s" repeatCount="indefinite"/>
          </use>
        </g>
        <path id="chain_st" stroke-width="1" stroke="#000" fill="transparent" stroke-dasharray="2.6 2.45" d="M21.3 13.5 H20 C11.4 13.5 11.4 26.5 20 26.5 H80 C89 26.5 89 13.5 80.8 13.5z" />
        <use class="rot" xlink:href="#cog">
          <animateTransform attributeType="xml" attributeName="transform" type="rotate"from="22.5 20 20" to="-337.5 20 20" dur="8s" repeatCount="indefinite"/>
        </use>
        <g transform="translate(60.3 0)">
          <use class="" xlink:href="#cog">
            <animateTransform attributeType="xml" attributeName="transform" type="rotate" from="22.5 20 20" to="-337.5 20 20" dur="8s" repeatCount="indefinite"/>
          </use>
        </g>
      </svg>

      原答案:

      您可以使用其他 svg 虚线路径并使用关键帧动画为 dash-offset 属性设置动画。

      这可以而且应该被简化/调整以用于“现实世界”:

      • 所有元素都可以包含在一个&lt;svg&gt; 标记中(这样会更简单,并且两个 cogs + chain 可以一起调整大小)
      • 链条和齿轮之间的同步并不完美,需要调整链条的速度/尺寸。

      #one {
        -webkit-animation: rotateClockwiseAnimation 5s linear infinite;
        /* Safari 4+ */
        -moz-animation: rotateClockwiseAnimation 5s linear infinite;
        /* Fx 5+ */
        -o-animation: rotateClockwiseAnimation 5s linear infinite;
        /* Opera 12+ */
        animation: rotateClockwiseAnimation 5s linear infinite;
        /* IE 10+, Fx 29+ */
      }
      #two {
        -webkit-animation: rotateAntiClockwiseAnimation 5s linear infinite;
        /* Safari 4+ */
        -moz-animation: rotateAntiClockwiseAnimation 5s linear infinite;
        /* Fx 5+ */
        -o-animation: rotateAntiClockwiseAnimation 5s linear infinite;
        /* Opera 12+ */
        animation: rotateAntiClockwiseAnimation 5s linear infinite;
        /* IE 10+, Fx 29+ */
        position: absolute;
        top: 30px;
        left: 42px;
        width: 80px;
      }
      @-webkit-keyframes rotateClockwiseAnimation {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
      @-moz-keyframes rotateClockwiseAnimation {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
      @-o-keyframes rotateClockwiseAnimation {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
      @keyframes rotateClockwiseAnimation {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
      @-webkit-keyframes rotateAntiClockwiseAnimation {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(-360deg);
        }
      }
      @-moz-keyframes rotateAntiClockwiseAnimation {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(-360deg);
        }
      }
      @-o-keyframes rotateAntiClockwiseAnimation {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(-360deg);
        }
      }
      @keyframes rotateAntiClockwiseAnimation {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(-360deg);
        }
      }
      /******************************************************************************/
      
      #chain {
        width: 650px;
        position: absolute;
        top: 24px;
        left: 35px;
      }
      .chain_st {
        stroke-dasharray: 1.5;
        stroke-dashoffset: 10;
        -webkit-animation: dash 18s infinite linear;
        -moz-animation: dash 18s infinite linear;
        -o-animation: dash 18s infinite linear;
        animation: dash 18s infinite linear;
      }
      @-webkit-keyframes dash {
        to {
          stroke-dashoffset: 100;
        }
      }
      @-moz-keyframes dash {
        to {
          stroke-dashoffset: 100;
        }
      }
      @-o-keyframes dash {
        to {
          stroke-dashoffset: 100;
        }
      }
      keyframes dash {
        to {
          stroke-dashoffset: 100;
        }
      }
      <svg id="one" style="width:50px" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 100">
        <defs>
          <circle id="c" cx="50" cy="50" r="30" stroke="#808080" fill="none" stroke-width="25" />
          <path id="d" stroke="#808080" stroke-width="16" d="M50 0, V15 M50 100, V85 M0 50, H15 M100 50, H85" />
        </defs>
        <use xlink:href="#c" />
        <use xlink:href="#d" />
        <use xlink:href="#d" transform="rotate(45, 50, 50)" />
      </svg>
      
      <svg id="two" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 100">
        <use xlink:href="#one" />
      </svg>
      <svg id="chain" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 70 10">
        <path class="chain_st" stroke-width="0.5" stroke="#000" fill="transparent" d="M60 1 Q65 1 65 5 Q65 9 60 9 H6 Q1 9 1 5 Q1 1 6 1z" />
      </svg>

      【讨论】:

      • FWIW,非 IE 版本在 Safari 上的效果比 IE 版本好很多。
      【解决方案5】:

      这种方法怎么样?我将 SVG 用于齿轮和传送带。齿轮按照您的示例旋转,但我使用stroke-dasharray 和动画stroke-dash-offset 使传送带移动。

      为了获得正确的传送带长度和冲刺时间,需要花费一些时间,如果您更改齿轮尺寸或传送带长度,则需要再次调整。

      #one{
        -webkit-animation: rotateClockwiseAnimation 4s linear infinite; /* Safari 4+ */
        -moz-animation:    rotateClockwiseAnimation 4s linear infinite; /* Fx 5+ */
        -o-animation:      rotateClockwiseAnimation 4s linear infinite; /* Opera 12+ */
        animation:         rotateClockwiseAnimation 4s linear infinite; /* IE 10+, Fx 29+ */
      
      }
      #two{
        -webkit-animation: rotateAntiClockwiseAnimation 4s linear infinite; /* Safari 4+ */
        -moz-animation:    rotateAntiClockwiseAnimation 4s linear infinite; /* Fx 5+ */
        -o-animation:      rotateAntiClockwiseAnimation 4s linear infinite; /* Opera 12+ */
        animation:         rotateAntiClockwiseAnimation 4s linear infinite; /* IE 10+, Fx 29+ */
      
       position:absolute;
          top:30px;
          left:42px;
          width:80px;
      }
      
      @-webkit-keyframes rotateClockwiseAnimation {
        0%   { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
      }
      @-moz-keyframes rotateClockwiseAnimation{
        0%   { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
      }
      @-o-keyframes rotateClockwiseAnimation {
        0%   { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
      }
      @keyframes rotateClockwiseAnimation {
        0%   { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
      }
      
      @-webkit-keyframes rotateAntiClockwiseAnimation {
        0%   { transform: rotate(0deg); }
        100% { transform: rotate(-360deg); }
      }
      @-moz-keyframes rotateAntiClockwiseAnimation {
        0%   { transform: rotate(0deg); }
        100% { transform: rotate(-360deg); }
      }
      @-o-keyframes rotateAntiClockwiseAnimation {
        0%   { transform: rotate(0deg); }
        100% { transform: rotate(-360deg); }
      }
      @keyframes rotateAntiClockwiseAnimation {
        0%   { transform: rotate(0deg); }
        100% { transform: rotate(-360deg); }
      }
      
      
      /******************************************************************************/
      
      #chain
      {
        -webkit-animation: conveyor 0.5s linear infinite; /* Safari 4+ */
        -moz-animation:    conveyor 0.5s linear infinite; /* Fx 5+ */
        -o-animation:      conveyor 0.5s linear infinite; /* Opera 12+ */
        animation:         conveyor 0.5s linear infinite; /* IE 10+, Fx 29+ */
      }
      
      
      @-webkit-keyframes conveyor {
          0%   { stroke-dashoffset: -9; }
          100% { stroke-dashoffset: 20.06; }
      }
      @-moz-keyframes conveyor {
          0%   { stroke-dashoffset: -9; }
          100% { stroke-dashoffset: 20.06; }
      }
      @-o-keyframes conveyor {
          0%   { stroke-dashoffset: -9; }
          100% { stroke-dashoffset: 20.06; }
      }
      @keyframes conveyor {  
          0%   { stroke-dashoffset: -9; }
          100% { stroke-dashoffset: 20.06; }
      }
      <svg width="100%" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 800 100">
          <defs>
              <circle id="c" cx="0" cy="0" r="30" stroke="#808080" fill="none" stroke-width="25"/>
              <path id="d" stroke="#808080" stroke-width="16" d="M0,-50 v15 M0,50 v-15 M-50,0 h15 M50,0 h-15"/>
      
              <g id="gear">
                <use xlink:href="#c"/>
                <use xlink:href="#d"/>
                <use xlink:href="#d" transform="rotate(45)"/>
              </g>
          </defs>
        
        <rect id="chain2"
                x="43" y="23" width="598" height="74" rx="37"
                stroke="gold" stroke-width="2" fill="none"/>
      
          <g transform="translate(27,27) scale(0.5)">
            <g id="one">
              <use xlink:href="#gear"/>
            </g>
          </g>
      
          <g transform="translate(80,60) scale(0.8)">
            <g id="two">
              <use xlink:href="#gear"/>
            </g>
          </g>
        
        <rect id="chain"
                x="43" y="23" width="598" height="74" rx="37"
                stroke="gold" stroke-width="5" fill="none"
                stroke-dasharray="14 15.06"/>
      </svg>

      【讨论】:

      • 谢谢。与我知道的 web-tiki 非常相似,但并行独立回答。
      • 如果你想让它看起来更像链子,你可以在虚线路径的背景中添加第二条更细的黄色路径。这条线看起来像是链条各部分之间的链接...... - 我做了一个编辑只是为了说明我的意思,你不需要接受
      • 齿轮现在已在 FF 中修复。
      • 我可以确认这个 sn-p 现在可以在 FF35 和 Chrome 40 中完美运行。但是,它在 IE11 中是静态图像。
      • IE 在应用于 SVG 元素时不支持 CSS3 动画。
      【解决方案6】:

      您可以尝试编辑 cog,使其更适合,而不是调整 div 边框以使其在 cog 上到位。它比 css 更容易操作图形。

      然后可能会将链式动画分成三到四个部分,以使其更加健壮。

      然后您可以调整 cog 和 chain 的速度以匹配,隐藏一半链,添加仅具有顶部和底部边框的 div 并在另一端执行相同但相反的操作。 (使用剪辑、位置和 z-index)。

      类似这样的:

      至少在理论上,这将是我的方法(更不用说我会使用 JS 而不是这个工作流程)。

      【讨论】:

        猜你喜欢
        • 2021-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多