【css动画】如何用纯css实现间隔动画

5,510 阅读4分钟

问题描述:今天到了公司,“德华”给我发来一段视频,是一个旋转的风车动画,看看有没有什么更好的实现方式么?脑爆就此开始……


目标:

小风车旋转时间为3s,间隔一段时间再旋转 小风车旋转时间为3s,间隔一段时间再旋转


首先拆解问题

分析问题:需要一个有间隔的无限循环动画,并需要有缓动函数

问题:1.css动画 2.有间隔 3.无限循环 4.缓动函数

当我们拆解完问题后可以想到目前的解决方案:

  • 通过animation-delay属性添加延迟属性
  • 设置整体动画时间(动画+延迟)在keyframes中将动画状态改为静止
  • 通过监听animationEnd在动画执行结束后延迟一段时间再此执行动画

因为需要用css所以第三点就直接pass了,那么我们尝试一下前两个方案

方案一:通过animation-delay添加延迟属性

.rotate{
  width:200px;
  height:200px;
  background:red;
  animation:rotate 3s ease-in-out infinite both;
  animation-delay:3s;//添加延迟属性
}
@keyframes rotate{
  100%{
    transform:rotate(1440deg)
  }
}

先用一个红色矩形模拟风车,并写了一个旋转4圈的的动画,旋转状态需要从静止缓慢开始,逐渐加快,最后再减速到停止,所以选用ease-in-out缓动函数,看下目前的效果: 通过animation-delay添加延迟时间

可以看出应用animation-delay属性,这样延迟时间只再第一次动画开始有效,后面的每次动画并没有间隔时间,所以animation-delay的方案pass了


方案二:通过控制keyframes动画阶段停止达成延迟效果,假如间隔时间为3s,所以动画总时长需要设置成6s,前3s矩形为静止状态

.rotate{
  width:200px;
  height:200px;
  background:red;
  animation:rotate 6s ease-in-out infinite both;
}
@keyframes rotate{
  0%{
    transform:rotate(0deg)
  }
  50%{
    transform:rotate(0deg)
  }
  100%{
    transform:rotate(1440deg)
  }
}

image

这个效果看起来还可以,可以基本上满足我们的需求,但是这是假设3s的间隔,如果是60s的间隔动画看起来就会不协调,因为曲线函数影响的过程是动画的总长,所以动画旋转的过程就只能受到一部分曲线函数的效果,所以这个方案也不是完美方案


Css的障眼法

我们需要缓动函数时间和动画时间相等,那就意味着keyframes中只能存在旋转的动画;需要间隔,但是delay只能在首次生效;从单纯的css属性层面看来不能解决目前的问题了,所以我们需要一种办法骗过你的眼睛,“眼见不一定为真”

<div class="box">
  <div class="rotate"></div>
</div>
<div class="front-box">
  <div class="rotateone"></div>
</div>

我们同时写两个元素相同的旋转元素,为了方便区分这里就用了不同的class,将下面的box元素设置上背景颜色,并将层级放到上面的box之上。这样看见的画面就只有一个静止的红色矩形,因为上面的元素被遮挡了。 image.png 此时,我们将上面box中的矩形设置上旋转动画,画面看起来也不会有任何变化,因为旋转元素还是在底层的

.rotate{
  ...
  animation:rotate 3s ease-in-out infinite both;
}
@keyframes rotate{
  100%{
    transform:rotate(1440deg)
  }
}

但是如果此时我们将下面box的opacity设置为0,就能看见上面box中正在旋转的元素,那么,我们只需要在这个元素刚好旋转完一圈的时候将遮挡元素opacity设置为1,然后经过一定间隔时间,再将其设置为0,这样看起来就像这个动画间隔一段时间又继续运动一样。并且动画间隔时间和缓动函数不受时长的影响,只要间隔时间是旋转动画的倍数即可。 image


完整代码:

.rotate{
  width:200px;
  height:200px;
  background:red;
  margin:0 auto;
  margin-top:100px;
  animation:rotate 3s ease-in-out infinite both;
}

.box{
  width:100%;
  height:500px;
  position:absolute;
  left:0;
  top:0;
}
.rotateone{
  width:200px;
  height:200px;
  background:red;
  margin:0 auto;
  margin-top:100px;
}
.front-box{
  width:100%;
  height:500px;
  position:absolute;
  left:0;
  top:0;
  background:#fff;
  z-index:2;
  opacity:0;
  animation:background 6s infinite linear both;
}
@keyframes rotate{
  100%{
    transform:rotate(1440deg)
  }
}
@keyframes background{
  0%{
    opacity:0;
  }
  49%{
    opacity:0;
  }
  50%{
    opacity:1;
  }

  100%{
     opacity:1;
  }
}

如果间隔时间太长,用1%会不精确,可以将百分比调整至0.1%为精度 预览地址:codepen.io/sanzang/pen…