首页 > 解决方案 > 在触发新动画之前完成一个 CSS 动画

问题描述

我正在尝试在悬停和取消悬停时创建运动图像。当鼠标悬停在图像上时,动画应该以一种方式流动,而当鼠标离开图像时,动画应该以另一种方式流动。

为此,我目前正在使用带有图像精灵的 css 动画。问题是,当鼠标在动画时间(1 秒)过去之前移动/离开图像时,图像会跳到下一个动画的开头,而不会完成上一个动画。

我尝试了各种方法来解决这个问题,但我似乎无法让它发挥作用(是的,这是我目前缺乏的技能)。有什么方法可以让动画始终在开始新动画之前完成?

http://jsfiddle.net/16jync0h/latest

HTML

<div id="navOpen"></div>

CSS

#navOpen {
  width: 64px;
  height: 64px;
  background: url('https://i.postimg.cc/hv0L4vsL/css-sprites.png') no-repeat left/cover;
}

.navOpenPlay {
  animation: Play 1s steps(24) 1 forwards;
}

.navOpenPlayBack {
  animation: PlayBack 1s steps(24) 1 backwards;
}

@keyframes Play {
  0% {
    background-position: 0px;
  }

  100% {
    background-position: -1536px;
  }
}

@keyframes PlayBack {
  0% {
    background-position: -1536px;
  }

  100% {
    background-position: 0px;
  }
}

Javascript/Jquery

 $("#navOpen").mouseover(function() {
   $(this).removeClass("navOpenPlayBack").addClass("navOpenPlay");
 });
 $("#navOpen").mouseout(function() {
   $(this).removeClass("navOpenPlay").addClass("navOpenPlayBack");
 });

标签: javascriptcss-animations

解决方案


如果您只是想在完成动画之前离开时反转动画,则可以将动画与 css:hover选择器链接:

#navOpen{
  width: 64px;
  height: 64px;
  /* reused the css-sprites because of laziness... */
  background: url('https://i.postimg.cc/hv0L4vsL/css-sprites.png') no-repeat left/cover; 
  transition: transform 2s;
}

#navOpen:hover{
  transform: rotate(90deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div id="navOpen"></div>

但是,如果你真的想在倒车前完成整个动画,那就有点冗长了。主要原因是每次动画结束时您都必须验证它是否仍处于正确状态。因此,我指的状态好像用户悬停在您的上方#navbar并且正在上课navOpenPlaynavOpenPlayBack分配。

// Javascript accessor to use "onanimationend"
let nav = document.getElementById("navOpen")
// First of all, we need to keep track if an animation is running or not. 
// This helps us, to prevent interruption of an animation through adding/removing css classes before the animation finished.
// We stock this information in the variable `navIsReady`.
let navIsReady = true

// Define a function to change the state. 
// If play === true, we add animation for onMouseOver by adding the corresponding css classes
function changeState(play) {
  // Verify if another animation is currently running
  if (navIsReady === true) {
    navIsReady = false // indicate that an animation is running
    if (play) { // previously called on mouseOver
      $(nav).removeClass("navOpenPlayBack").addClass("navOpenPlay")
    } else { // previously called on mouseOut
      $(nav).removeClass("navOpenPlay").addClass("navOpenPlayBack")
    }
  }
}

// Define a callback when an animation ends. 
// The callback checks that the object is in the right state after finishing the animation
nav.onanimationend = function(event) {
  navIsReady = true // allow animations again
  // Check if the navigation is in the correct state after finishing the animation 
  if ($(nav).is(':hover') && (!$(nav).hasClass("navOpenPlay"))) {
    // False state. Change the state through a call
    changeState(true)
  } else if (!$(nav).is(':hover') && (!$(nav).hasClass("navOpenPlayBack"))) {
    // False state. Change the state through a call
    changeState(false)
  }
};

// Hook MouseOver and MouseOut events
$(nav).mouseover(function() {
  changeState(true)
});
$(nav).mouseout(function() {
  changeState(false)
});
#navOpen {
  width: 64px;
  height: 64px;
  background: url('https://i.postimg.cc/hv0L4vsL/css-sprites.png') no-repeat left/cover;
}

.navOpenPlay {
  animation: Play 1s steps(24) 1 forwards;
}

.navOpenPlayBack {
  animation: PlayBack 1s steps(24) 1 backwards;
}

@keyframes Play {
  0% {
    background-position: 0px;
  }
  100% {
    background-position: -1536px;
  }
}

@keyframes PlayBack {
  0% {
    background-position: -1536px;
  }
  100% {
    background-position: 0px;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div id="navOpen"></div>


推荐阅读