在触发新动画之前完成CSS动画

时间:2020-04-28 13:05:39

标签: javascript css-animations

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

为此,我目前正在使用带有图片精灵的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");
 });

2 个答案:

答案 0 :(得分:0)

如果您只是想在离开动画之前在倒转动画之前完成动画,则可以将动画与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>

但是,如果您真的想在反转之前完成整个动画,则它会比较冗长。主要原因是您必须在动画结束时每次都验证动画是否仍然处于正确的状态。在这里,我指的是 state ,就好像用户将鼠标悬停在您的#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>

答案 1 :(得分:0)

如@NoSkill所述,您可以使用onanimationend事件来侦听动画何时完成,并在其中添加自定义逻辑。

然后可以将其与队列结合使用,以跟踪当前正在制作动画的内容以及接下来要制作动画的内容。

查看示例:

let isAnimating = false;
const animationQueue = [];

function queueAnimation(cssClass) {
  animationQueue.push(cssClass);
  if (!isAnimating) {
    triggerAnimation();
  }
}

function triggerAnimation() {
  const cssClass = animationQueue.shift();
  if (cssClass) {
    $("#navOpen").attr('class', '').addClass(cssClass);
    isAnimating = true;
  }
}

$("#navOpen").mouseover(function() {
  queueAnimation('navOpenPlay');
});
$("#navOpen").mouseout(function() {
  queueAnimation('navOpenPlayBack');
});
$("#navOpen").on("animationend", function() {
  isAnimating = false;
  triggerAnimation();
});
#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.1/jquery.min.js"></script>
<div id="navOpen"></div>