How To Sync CSS Animations Across Multiple Elements?

Solution 1:

I don't think its possible natively, but you can actually hack similar functionality by using a bouncing wrapper and some position altering

html:

<div id="bouncywrap">
    <div id="bouncy01">Drip</div>
    <div id="bouncy02">droP</div>
<div>

CSS:

@-webkit-keyframes bounce {
    0% { padding-top:1px;}
/* using padding as it does not affect position:relative of sublinks
 * using 0 instead of 0 b/c of a box-model issue,
 * on kids wiht margin, but parents without margin, just try out
 */
    50% { padding-top:5px;} /*desired value +1*/
    100% { padding-top:1px;}
}

#bouncy01,
#bouncy02 {
    margin:10px;
    background: #ff0000;
    padding: 10px;
    border: 1px solid #777;
    width:30px;
       position:absolute;
}
#bouncywrap {
    -webkit-animation:bounce 0.125s ease-in-out infinite;
    position:relative;
    width:140px;
    height:50px;
/*    background:grey; /*debug*/
}
#bouncy02 {
    background: #ffff00;
    left:60px;
    top:2px; /*half of desired value, just a fix for the optic*/
}
#bouncy02:hover {
    position:relative; /*here happens the magic*/
    top:0px;
}

demo http://jsfiddle.net/A92pU/1/

Solution 2:

I was looking for an alternative solution to those proposed here because:

  1. I am animating a background color - which can't use the positioning magic in the accepted answer.
  2. I wanted to avoid calculations for a very simple animation in my app.

After further research I came across this module by bealearts.

It exposes a very neat API that lets you keep an animation in sync across the app by referring to it's name:

import sync from 'css-animation-sync';
sync('spinner');

Since this seemed a little too good to be true, I tested the library (which is a single short file) in this fiddle and am happy to report it works (hover on the third image and see that I quickly syncs to the second image's animation) :).

Credit: I used the animation from this fiddle by Simurai as a basis for my fiddle.

If anyone wants to replicate the mechanism behind this synchronisation, the code is open, but in it's essence, it uses events listeners for the animation itself as sync points:

window.addEventListener('animationstart', animationStart, true);
window.addEventListener('animationiteration', animationIteration, true);

Hope this helps the next person looking for a solution to this problem.

Solution 3:

The Web Animations API now allows to control animations very precisely and quite easily.

There are various ways to declare a Web Animation, but since here we started with CSS, here is how to hook to it:

// when the animation starts
document.querySelector("#bouncy02")
  .addEventListener("animationstart", (evt) => {
  // double check it the animation we wanted
  if (evt.animationName === "bounce") {
    // retrieve both Animation objects
    const myAnim = findAnimByName(evt.target, "bounce");
    const otherAnim = findAnimByName(document.querySelector("#bouncy01"), "bounce");
    // update mine to act as if it started
    // at the same time as the first one
    myAnim.startTime = otherAnim.startTime;
  }
});
// simple helper to find an Animation by animationName
function findAnimByName(elem, name) {
  // get all the active animations on this element
  const anims = elem.getAnimations();
  // return the first one with the expected animationName
  return anims.find((anim) => anim.animationName === name);
}
@keyframes bounce {
    0% {transform: translateY(0px);}
    25% {transform: translateY(-2px);}
    50% {transform: translateY(-4px);}
    75% {transform: translateY(-2px);}
    100% {transform: translateY(0px);}
}
#bouncy01,
#bouncy02 {
    margin:10px;
    float: left;
    background: #ff0000;
    padding: 10px;
    border: 1px solid #777;
}
#bouncy01 {
    animation:bounce 0.25s ease-in-out infinite alternate;
}
#bouncy02 {
    background: #ffff00;
}
#bouncy02:hover {
    animation:bounce 0.25s ease-in-out infinite alternate;
}
<div id="bouncy01">Drip</div>
<div id="bouncy02">droP</div>

Note that while it's surprisingly not yet that propular, this API actually exists for some times now, and its browser support (all except IE) is quite good.

Solution 4:

Looks like you can just stack two of the yellow ones and switch visibility on :hover through a parent element.

You need the animation to always be running otherwise you'll run into the sync issue you've got.

I modified your code a bit to get this.

Solution 5:

You could use a setInterval to maintain the animation state of the first animation and give the other animation a negative delay to seek to its matching keyframe on mouse-over.

Read about the state-maintaining-interval-thing here, at the "Manipulating CSS Animations" section; read about the negative delay to seek here.