如何播放反向关键帧动画以悬停?

Posted

技术标签:

【中文标题】如何播放反向关键帧动画以悬停?【英文标题】:How to play reverse keyframe animation for hover off? 【发布时间】:2016-05-13 19:38:53 【问题描述】:

我正在尝试构建一个下拉菜单,首先下拉菜单会向下滑动,然后从底部逐一显示菜单项。在悬停时,它看起来不错,但我不知道如何在悬停时顺利播放。我要做的是先隐藏第二个子菜单项,然后是第一个子菜单项,然后下拉菜单向上滑动。

我尝试的是在默认情况下将具有延迟的隐藏动画关键帧添加到下拉列表中,并在悬停时添加具有 0 延迟的显示动画。但是你可以看到它不能正常工作。(它也会在页面加载时动画,这对我不利)

ul li ul 
  -webkit-animation: hideDropdown 1s;
  -webkit-animation-delay: 0.5s;


ul li:hover ul 
  -webkit-animation: showDropdown 1s forwards;
  -webkit-animation-delay: 0s;

Fiddle Demo(使用 SCSS)

【问题讨论】:

获取动画来实现反向效果总是非常复杂。为什么不改用过渡? 因为我想一个一个地展示项目,所以一切都有一点延迟。所以当我悬停时,最后一个项目有 1 秒的延迟,但是在悬停时,它应该是第一个隐藏的。不确定这是否可以通过转换来实现。 【参考方案1】:

有时使用animation-direction: alternate; 来反转当前动画会更容易。

【讨论】:

【参考方案2】:

正如我在 cmets 中对问题所指出的,产生动画的反向效果总是很复杂。这并非不可能,只是需要额外的编码(如额外的关键帧)和调整所有相关属性才能达到完美的效果。

如果还需要实现反向效果,使用transition 是最佳选择。完全有可能在使用过渡之后产生效果,这与使用动画的方式非常相似。在:hover 选择器中,应用transition 设置,使:first-child 的延迟低于第二个(以及后续子级),使其首先出现,并在默认选择器中应用transition 设置,使@ 987654328@ 的延迟比其他的要高。

使用过渡还可以避免在页面加载时显示动画。这是因为只有在状态从一个状态更改到另一个状态时才会发生转换。

body 
  background: #f1f1f1;


ul 
  height: 100px;
  position: relative;
  background: #fff;

ul li 
  display: block;
  float: left;
  margin: 0;
  list-style-type: none;
  padding: 0;

ul li a 
  display: block;
  height: 100px;
  line-height: 100px;
  padding: 0 20px;

ul li ul 
  position: absolute;
  left: 0px;
  right: 0px;
  top: 100px;
  height: 0px;
  opacity: 0;
  visibility: hidden;
  transition: all 1s 0.5s;

ul li ul li 
  position: absolute;
  left: 50px;
  bottom: 0px;
  width: 200px;
  overflow: hidden;
  height: 100px;

ul li ul li a 
  display: block;
  height: 100px;
  background: red;
  padding: 0 20px;
  position: absolute;
  opacity: 0;
  transform: translate(0px, 100px);
  transition: all 1s 0.2s;

ul li ul li:first-child a 
  transition-delay: 0.4s;

ul li ul li:last-child 
  left: 250px;

ul li ul li:last-child a 
  transition-delay: 0.2s;

ul li:hover ul 
  height: 100px;
  opacity: 1;
  visibility: visible;
  transition: all 1s 0s;

ul li:hover ul li a 
  opacity: 1;
  transform: translate(0px, 0px);
  transition: all 1s 0.2s;

ul li:hover ul li:last-child a 
  transition-delay: 0.4s;
<ul>
  <li>
    <a href="#">Dropdown</a>
    <ul>
      <li><a href="">First</a></li>
      <li><a href="">second</a></li>
    </ul>
  </li>
  <li><a href="">Not dropdown</a></li>
</ul>

注意:在上面的 sn-p 中,我使用了编译后的 CSS 并删除了所有 -webkit- 前缀,使其在所有浏览器上都可以查看。如果你想要带前缀的 SCSS 代码,你可以找到它here。


说了这么多,我仍然想解释是什么造成了您的animation 演示的问题。你做了很多正确的工作,你几乎接近实现你想要的。

动画的工作方式是,一旦应用动画的选择器不再适用,动画就会被移除(丢失),并且元素会立即恢复到其默认或原始状态。即使在悬停时应用了另一个动画,在反向动画的关键帧设置中指定的属性也不会应用直到它们的延迟时间过去。这意味着ula 快速恢复为不可见(通过opacityvisibility)设置,在动画开始时再次变为可见(通过from 关键帧),然后动画到结束状态(再次通过to 关键帧不可见)。

解决此问题的方法是将animation-fill-mode: backwards 设置为ula(未悬停状态)。此设置意味着该元素即使在延迟期间也会采用from 关键帧中指定的属性。

body 
  background: #f1f1f1;

ul 
  height: 100px;
  position: relative;
  background: #fff;

ul li 
  display: block;
  float: left;
  margin: 0;
  list-style-type: none;
  padding: 0;

ul li a 
  display: block;
  height: 100px;
  line-height: 100px;
  padding: 0 20px;

ul li ul 
  position: absolute;
  left: 0px;
  right: 0px;
  top: 100px;
  height: 0px;
  visibility: hidden;
  animation: hideDropdown 1s;
  animation-delay: 0.5s;
  animation-fill-mode: backwards;

ul li ul li 
  position: absolute;
  left: 50px;
  bottom: 0px;
  width: 200px;
  overflow: hidden;
  height: 100px;

ul li ul li a 
  display: block;
  height: 100px;
  background: red;
  padding: 0 20px;
  position: absolute;
  opacity: 0;
  transform: translate(0px, 100px);
  animation: hideDropdownItem 1s 0.2s;
  animation-fill-mode: backwards;

ul li ul li:first-child a 
  animation-delay: 0.4s;

ul li ul li:last-child 
  left: 250px;

ul li ul li:last-child a 
  animation-delay: 0.2s;

ul li:hover ul 
  animation: showDropdown 1s forwards;
  animation-delay: 0s;

ul li:hover ul li a 
  animation: showDropdownItem 1s forwards 0.2s;

ul li:hover ul li:last-child a 
  animation-delay: 0.4s;

@keyframes showDropdown 
  from 
    opacity: 0;
    height: 0;
    visibility: hidden;
  
  to 
    height: 100px;
    opacity: 1;
    visibility: visible;
  

@keyframes hideDropdown 
  from 
    height: 100px;
    opacity: 1;
    visibility: visible;
  
  to 
    opacity: 0;
    height: 0;
    visibility: hidden;
  

@keyframes showDropdownItem 
  from 
    opacity: 0;
    transform: translate(0px, 100px);
  
  to 
    opacity: 1;
    transform: translate(0px, 0px);
  

@keyframes hideDropdownItem 
  from 
    opacity: 1;
    transform: translate(0px, 0px);
  
  to 
    opacity: 0;
    transform: translate(0px, 100px);
  
<ul>
  <li>
    <a href="#">Dropdown</a>
    <ul>
      <li><a href="">First</a>
      </li>
      <li><a href="">second</a>
      </li>
    </ul>
  </li>
  <li><a href="">Not dropdown</a>
  </li>
</ul>

注意: 和第一个 sn-p 一样,上面的也是使用没有前缀的纯 CSS。带前缀的 SCSS 版本可用here。

但如前所述,这不会阻止动画在页面加载时可见。阻止这种情况的唯一方法是使用 javascript

【讨论】:

谢谢!在 Safari 中的第一个子项目对我来说有一个问题......在 Chrome 中工作正常,我会尝试为此找到解决方案。 好的,如果我有 Safari,我会帮你的。 @passatgt:我也知道您的原始动画无法正常工作的原因。

以上是关于如何播放反向关键帧动画以悬停?的主要内容,如果未能解决你的问题,请参考以下文章

悬停时继续 CSS3 关键帧动画/悬停时停止重复

使用变换时,悬停时的 CSS 动画停留在最后一个关键帧:旋转

关键帧动画不适用于:moz 中的悬停元素和

悬停时播放 CSS 动画,悬停时暂停

ThreeJS——关键帧编辑及解析播放

悬停关键帧 CSS3 缓入