【问题标题】:Wait for CSS Animation to Complete等待 CSS 动画完成
【发布时间】:2016-04-25 16:26:57
【问题描述】:

我正在制作带有动画的定向翻转。我以为我找到了使用以下绑定的解决方案,但我没有注意到以下问题...

bind( 'transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', function() {

问题在于,如果您将鼠标悬停在左上方,那么动画会沿对角线翻转或旋转 - 您可以快速悬停 div 的多个部分,它会做一些疯狂的事情。我想要发生的是动画完成或重置,而不是立即尝试处理下一个悬停动画。所有现代浏览器(IE Edge / 11、Chrome、Firefox。这些都是我测试过的)都会出现这个问题。

我是不是走错了路?如何让我的 javascript 等待翻转动画完成?


相关的问题是,有时无论哪个方向,动画都会卡在从左到右或从上到下的动画,因为它没有等待完成,所以它可以删除其他类。

jQuery(document).ready(function() {
  $('.galleryWrapper.bot .gallery-item').hover(
    function(e) {
      $(this).unbind('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd');
      $(this).removeClass('rtl');
      var w = $(this).width();
      var h = $(this).height();
      var x = (e.pageX - this.offsetLeft - (w / 2)) * (w > h ? (h / w) : 1);
      var y = (e.pageY - this.offsetTop - (h / 2)) * (h > w ? (w / h) : 1);
      var direction = Math.round(Math.atan2(y, x) / 1.57079633 + 5) % 4;

      switch (direction) {
        case 0: // Top
          $(this).addClass('utd');
          break;

        case 1: // Right
          $(this).addClass('rtl');
          break;

        case 2: // Bottom
          $(this).addClass('dtu');
          break;

        case 3: // Left
          $(this).addClass('ltr');
          break;
      }
    },

    function(e) {
      $(this).on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', function() {
        $(this).removeClass('utd rtl dtu ltr').addClass('rtl');
      });
    }
  );
});
.galleryWrapper {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-wrap: wrap;
  flex-wrap: wrap;
}
.galleryWrapper .gallery-item {
  margin: 10px;
  -webkit-perspective: 1000;
  -moz-perspective: 1000;
  -ms-perspective: 1000;
  perspective: 1000;
  -ms-transform: perspective(1000px);
  -moz-transform: perspective(1000px);
  -moz-transform-style: preserve-3d;
  -ms-transform-style: preserve-3d;
}
.galleryWrapper .gallery-item,
.galleryWrapper .gallery-item .item .side {
  width: 100px;
  height: 100px;
  box-sizing: border-box;
}
.galleryWrapper .gallery-item .item {
  position: relative;
  -webkit-transition: 0.6s;
  -webkit-transform-style: preserve-3d;
  -ms-transition: 0.6s;
  -moz-transition: 0.6s;
  -moz-transform: perspective(1000px);
  -moz-transform-style: preserve-3d;
  -ms-transform-style: preserve-3d;
  transition: 0.6s;
  transform-style: preserve-3d;
}
.galleryWrapper .gallery-item .item .side {
  position: absolute;
  top: 0;
  left: 0;
  -webkit-backface-visibility: hidden;
  -moz-backface-visibility: hidden;
  -ms-backface-visibility: hidden;
  backface-visibility: hidden;
}
.galleryWrapper .gallery-item .item-front {
  z-index: 2;
  background-color: red;
}
.galleryWrapper .gallery-item .item-back {
  background-color: blue;
}
.galleryWrapper .gallery-item.ltr .item-front {
  -webkit-transform: rotateY(0deg);
  -moz-transform: rotateY(0deg);
  -o-transform: rotateY(0deg);
  -ms-transform: rotateY(0deg);
  transform: rotateY(0deg);
}
.galleryWrapper .gallery-item.ltr .item-back {
  -webkit-transform: rotateY(180deg);
  -moz-transform: rotateY(180deg);
  -o-transform: rotateY(180deg);
  -ms-transform: rotateY(180deg);
  transform: rotateY(180deg);
}
.galleryWrapper .gallery-item.ltr:hover .item {
  -webkit-transform: rotateY(180deg);
  -moz-transform: rotateY(180deg);
  -o-transform: rotateY(180deg);
  -ms-transform: rotateY(180deg);
  transform: rotateY(180deg);
}
.galleryWrapper .gallery-item.rtl .item-front {
  -webkit-transform: rotateY(0deg);
  -moz-transform: rotateY(0deg);
  -o-transform: rotateY(0deg);
  -ms-transform: rotateY(0deg);
  transform: rotateY(0deg);
}
.galleryWrapper .gallery-item.rtl .item-back {
  -webkit-transform: rotateY(-180deg);
  -moz-transform: rotateY(-180deg);
  -o-transform: rotateY(-180deg);
  -ms-transform: rotateY(-180deg);
  transform: rotateY(-180deg);
}
.galleryWrapper .gallery-item.rtl:hover .item {
  -webkit-transform: rotateY(-180deg);
  -moz-transform: rotateY(-180deg);
  -o-transform: rotateY(-180deg);
  -ms-transform: rotateY(-180deg);
  transform: rotateY(-180deg);
}
.galleryWrapper .gallery-item.utd .item,
.galleryWrapper .gallery-item.dtu .item {
  transform-origin: 100% 50px;
}
.galleryWrapper .gallery-item.dtu .item-front {
  -webkit-transform: rotateX(0deg);
  -moz-transform: rotateX(0deg);
  -o-transform: rotateX(0deg);
  -ms-transform: rotateX(0deg);
  transform: rotateX(0deg);
}
.galleryWrapper .gallery-item.dtu .item-back {
  -webkit-transform: rotateX(180deg);
  -moz-transform: rotateX(180deg);
  -o-transform: rotateX(180deg);
  -ms-transform: rotateX(180deg);
  transform: rotateX(180deg);
}
.galleryWrapper .gallery-item.dtu:hover .item {
  -webkit-transform: rotateX(180deg);
  -moz-transform: rotateX(180deg);
  -o-transform: rotateX(180deg);
  -ms-transform: rotateX(180deg);
  transform: rotateX(180deg);
}
.galleryWrapper .gallery-item.utd .item-front {
  -webkit-transform: rotateX(0deg);
  -moz-transform: rotateX(0deg);
  -o-transform: rotateX(0deg);
  -ms-transform: rotateX(0deg);
  transform: rotateX(0deg);
}
.galleryWrapper .gallery-item.utd .item-back {
  -webkit-transform: rotateX(-180deg);
  -moz-transform: rotateX(-180deg);
  -o-transform: rotateX(-180deg);
  -ms-transform: rotateX(-180deg);
  transform: rotateX(-180deg);
}
.galleryWrapper .gallery-item.utd:hover .item {
  -webkit-transform: rotateX(-180deg);
  -moz-transform: rotateX(-180deg);
  -o-transform: rotateX(-180deg);
  -ms-transform: rotateX(-180deg);
  transform: rotateX(-180deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="galleryWrapper bot">

  <div class="gallery-item">
    <div class="item">
      <div class="side item-front"></div>
      <div class="side item-back"></div>
    </div>
  </div>
  <!-- class="gallery-item" -->

  <div class="gallery-item">
    <div class="item">
      <div class="side item-front"></div>
      <div class="side item-back"></div>
    </div>
  </div>
  <!-- class="gallery-item" -->

  <div class="gallery-item">
    <div class="item">
      <div class="side item-front"></div>
      <div class="side item-back"></div>
    </div>
  </div>
  <!-- class="gallery-item" -->

  <div class="gallery-item">
    <div class="item">
      <div class="side item-front"></div>
      <div class="side item-back"></div>
    </div>
  </div>
  <!-- class="gallery-item" -->

</div>
<!-- class="galleryWrapper" -->

View on JSFiddle

我的动画基于CSS Flip by David Walsh

【问题讨论】:

  • @Harry 顶部版本的翻转工作正常,底部版本在所有浏览器的最新版本中都有问题。如果我快速将鼠标悬停在左侧然后顶部或类似效果,它会尝试对角翻转它,或者有时它会卡在某个动画类上(例如 rtlutd )。
  • 是的,我确实设法解决了这个问题(并在我对上一条评论的编辑中注明)。如果您在问题本身中指定以避免像我这样的 cmets 会更好:)
  • @Harry 你是对的。我删除了第一个工作示例,并将原始想法与我的测试用例一起添加到问题的底部。谢谢!
  • 你无意中做了一个非常有趣的玩具。我只花了五分钟把它们转遍整个地方!
  • 为了清楚起见,您是否想说每次悬停主持悬停的过渡应该在接下来的几个小时过渡之前完成?。我删除了我的答案,因为当我在每次新悬停时检查悬停时在我的示例中,过渡不会作为一个全新的循环完成。但是如果单击过渡,一个动画的结束被成功检查,然后下一个开始

标签: jquery css animation


【解决方案1】:

Solved Demo

Second Demo with fast paced movement

Fiddle with .on() method instead of .bind()

Example given in your question with simple modifications according to my way

[在 chrome 版本 50.0.2661.87 m(64 位)上测试, Opera 36.0.2130.65,Firefox 45.0.2 IE 版本 11.0.9600.17843]

我保持简单,让事情变得容易引人注目我使用了延迟 3 秒的简单反弹动画,而您的逻辑发生的问题是每次新悬停时您都开始新动画(每次计算位置并切换语句添加类)。我只是结束了它,所以在每个过渡效果完成后,只有新的过渡才会开始,所以你所说的关于右左运动的问题,然后是突然的顶部,导致两个过渡都发生而没有一个完成另一个永远不会发生

  1. 默认为 div 添加完成的类
  2. 然后我所做的就是在 switch 里面检查它是否有那个类
  3. 输入if case后删除
  4. 下一步像你一样添加css类
  5. Next 里面的 bind 方法删除动画类
  6. 最后添加完成的类

$(".box").on("webkitAnimationEnd oAnimationEnd msAnimationEnd animationend", function(e) {

  $(this).removeClass("animated animatedL animatedR animatedT");
  $(this).addClass("finished");
})

$(".box").hover(function(e) {
  var $class = $(this).hasClass("finished");
  //$(this).addClass("animated");    
  /* */
  var w = $(this).width();
  var h = $(this).height();
  var x = (e.pageX - this.offsetLeft - (w / 2)) * (w > h ? (h / w) : 1);
  var y = (e.pageY - this.offsetTop - (h / 2)) * (h > w ? (w / h) : 1);
  var direction = Math.round(Math.atan2(y, x) / 1.57079633 + 5) % 4;

  switch (direction) {
    case 0: // Top
      if ($class) {
        $(this).removeClass("finished");
        $(this).addClass('animatedT');
      }
      break;

    case 1: // Right
      if ($class) {
        $(this).removeClass("finished");
        $(this).addClass('animatedR');
      }
      break;

    case 2: // Bottom
      if ($class) {
        $(this).removeClass("finished");
        $(this).addClass('animated');
      }
      break;

    case 3: // Left
      if ($class) {
        $(this).removeClass("finished");
        $(this).addClass('animatedL');
      }


  }
})
@-webkit-keyframes bounce {
  0% {
    top: 0;
    animation-timing-function: ease-out;
  }
  17% {
    top: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    top: 0;
    animation-timing-function: ease-out;
  }
  51% {
    top: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    top: 0px;
    animation-timing-function: ease-out;
  }
  85% {
    top: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    top: 0;
  }
}
@-moz-keyframes bounce {
  0% {
    top: 0;
    animation-timing-function: ease-out;
  }
  17% {
    top: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    top: 0;
    animation-timing-function: ease-out;
  }
  51% {
    top: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    top: 0px;
    animation-timing-function: ease-out;
  }
  85% {
    top: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    top: 0;
  }
}
@keyframes bounce {
  0% {
    top: 0;
    animation-timing-function: ease-out;
  }
  17% {
    top: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    top: 0;
    animation-timing-function: ease-out;
  }
  51% {
    top: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    top: 0px;
  }
  85% {
    top: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    top: 0;
  }
}
@-webkit-keyframes bounceL {
  0% {
    left: 0;
    animation-timing-function: ease-out;
  }
  17% {
    left: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    left: 0;
    animation-timing-function: ease-out;
  }
  51% {
    left: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    left: 0px;
    animation-timing-function: ease-out;
  }
  85% {
    left: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    left: 0;
  }
}
@-moz-keyframes bounceL {
  0% {
    left: 0;
    animation-timing-function: ease-out;
  }
  17% {
    left: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    left: 0;
    animation-timing-function: ease-out;
  }
  51% {
    left: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    left: 0px;
    animation-timing-function: ease-out;
  }
  85% {
    left: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    left: 0;
  }
}
@keyframes bounceL {
  0% {
    left: 0;
    animation-timing-function: ease-out;
  }
  17% {
    left: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    left: 0;
    animation-timing-function: ease-out;
  }
  51% {
    left: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    left: 0px;
  }
  85% {
    left: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    left: 0;
  }
}
@-webkit-keyframes bounceR {
  0% {
    right: 0;
    animation-timing-function: ease-out;
  }
  17% {
    right: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    right: 0;
    animation-timing-function: ease-out;
  }
  51% {
    right: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    right: 0px;
    animation-timing-function: ease-out;
  }
  85% {
    right: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    right: 0;
  }
}
@-moz-keyframes bounceR {
  0% {
    right: 0;
    animation-timing-function: ease-out;
  }
  17% {
    right: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    right: 0;
    animation-timing-function: ease-out;
  }
  51% {
    right: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    right: 0px;
    animation-timing-function: ease-out;
  }
  85% {
    right: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    right: 0;
  }
}
@keyframes bounceR {
  0% {
    right: 0;
    animation-timing-function: ease-out;
  }
  17% {
    right: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    right: 0;
    animation-timing-function: ease-out;
  }
  51% {
    right: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    right: 0px;
  }
  85% {
    right: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    right: 0;
  }
}
@-webkit-keyframes bounceT {
  0% {
    top: 0;
    animation-timing-function: ease-out;
  }
  17% {
    top: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    top: 0;
    animation-timing-function: ease-out;
  }
  51% {
    top: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    top: 0px;
    animation-timing-function: ease-out;
  }
  85% {
    top: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    top: 0;
  }
}
@-moz-keyframes bounceT {
  0% {
    top: 0;
    animation-timing-function: ease-out;
  }
  17% {
    top: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    top: 0;
    animation-timing-function: ease-out;
  }
  51% {
    top: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    top: 0px;
    animation-timing-function: ease-out;
  }
  85% {
    top: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    top: 0;
  }
}
@keyframes bounceT {
  0% {
    top: 0;
    animation-timing-function: ease-out;
  }
  17% {
    top: 15px;
    animation-timing-function: ease-in;
  }
  34% {
    top: 0;
    animation-timing-function: ease-out;
  }
  51% {
    top: 8px;
    animation-timing-function: ease-in;
  }
  68% {
    top: 0px;
  }
  85% {
    top: 3px;
    animation-timing-function: ease-in;
  }
  100% {
    top: 0;
  }
}
#container {
  position: relative;
}
.box {
  position: relative;
  float: left;
  background: #f00;
  width: 50px;
  height: 50px;
  margin-right: 5px;
  margin: 50px;
}
.box.animated {
  -moz-animation: bounce .5s;
  -webkit-animation: bounce .5s;
  animation: bounce .5s;
}
.box.animatedL {
  -moz-animation: bounceL .5s;
  -webkit-animation: bounceL .5s;
  animation: bounceL .5s;
}
.box.animatedR {
  -moz-animation: bounceR .5s;
  -webkit-animation: bounceR .5s;
  animation: bounceR .5s;
}
.box.animatedT {
  -moz-animation: bounceT .5s;
  -webkit-animation: bounceT .5s;
  animation: bounceT .5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<div id="container">
  <div class="box finished"></div>
  <div class="box finished"></div>
  <div class="box finished"></div>
  <div class="box finished"></div>
</div>

编辑-

更改.bind() 方法,而不是使用.on(),因为不推荐使用绑定

$(".box").on("webkitAnimationEnd oAnimationEnd msAnimationEnd animationend", function (e){

 $(this).removeClass("animated animatedL animatedR animatedT");
  $(this).addClass("finished");  
  })

在你原来的小提琴中,我只是做了一些更改,我只是添加了类名
.finished,然后在开关盒内添加了一个 if 循环,以检查悬停的元素是否在其中具有 .finished 类使用 .hasClass() 的类列表,如果为 true,则返回布尔值,然后添加动画类,并且在您的 .on() 函数中,我刚刚添加了 .finished 类,指示动画结束。此序列可防止动画重叠,然后再次为安全起见,您可以在过渡结束后添加 100 毫秒的延迟

【讨论】:

  • 整洁!这很好用,而且非常简单。不过,我需要等待 24 小时才能获得赏金。
  • 好的,谢谢@Howdy_McGee 我花了一些时间才意识到它真的很简单直接
【解决方案2】:

前几天我遇到了类似的问题,我想完成一个轮换。我得到了它的工作原理,也许它可以在某种程度上帮助你。

function completeRotate(obj){
        obj.one('animationiteration webkitAnimationIteration', function() {
        $(this).off('animationiteration webkitAnimationIteration');
        $(this).removeClass('rotating');
    });
};

请参阅此处的参考资料http://api.jquery.com/one/

和平与爱

【讨论】:

    【解决方案3】:

    您不需要 Javascript 或 Jquery。悬停时使用 0.6 秒的过渡时间,悬停时使用 0.01 的动画时间。这样,动画将立即几乎将自身重置到其原始位置并停止时髦的行为。请记住,您可以对悬停和未悬停的元素具有不同的动画效果。这同样适用于计时。

    因此,针对您的示例,将元素上的时间从 0.6 更改为 0.01(或另一个同样很小的数字,不确定它可以走多低...)并将 0.6 的时间添加到您的 :hover元素。这样,动画将在悬停时按照您的预期运行,如果悬停,动画将恢复到原始状态。

    关于 Javascript 知道动画何时完成,不幸的是,这取决于您的手动计时。您可以使用 0.6 秒的超时,当您悬停时激活该超时,然后在超时后执行您想做的任何事情。我发现将 CSS 和 Javascript 一起计时有点像噩梦,你通常应该选择其中一个。如果你想要一个超时方法的例子,请告诉我,我会用一个来编辑答案。

    编辑您的评论:

    是的,我认为你的做法是错误的。正如我之前所说,CSS 和 Javascript 的时间安排很棘手,但这里的根本问题是 CSS 无法与您的页面通信来告诉您动画当前处于活动状态。

    如果动画已经开始,您正在寻找的是限制动画。这不能用 CSS 完成,但可以用 Javascript。

    动画不是在 CSS 类中,而是在 Javascript 中。然后,在 Javascript 中创建一个悬停事件,该事件将添加一个类,例如“活动”。悬停时,使用 jQuery 的 animate 函数将动画添加到元素并在 done 函数中,删除类 'active':

    Element.on('hover',function(){
        if( !$(this).hasClass('active'))
            $(this).addClass('active').animate({'rotate:etc'},600,function(){
                $(this).removeClass('active')
            })
    }
    

    如果用户过早地悬停,您可以使用 mouseout 事件来移除该类。

    【讨论】:

    • 这是一种方法,但我不一定希望它立即重置。我想要它做的不是处理从上到下的动画,同时也处理从左到右的动画。我仍然希望它正常过渡回原来的位置。
    • 好吧,我想我误解了这个问题。让我编辑我的答案,有一个解决方案,但我认为这里没有足够的字符。
    猜你喜欢
    • 2012-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    • 2017-03-15
    相关资源
    最近更新 更多