JSFiddle - React, Tailwind, and code Playground
HTML
<body>
<div style="display:none">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="384" height="304">
<svg id="icon-ellipsis" class="icon-ellipsis" width="100%" height="100%" viewBox="0 0 24 24">
<path class="icon-ellipsis-dot" d="M6 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
<path class="icon-ellipsis-dot" d="M14 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
<path class="icon-ellipsis-dot" d="M22 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
</svg>
</svg>
</div>
<!-- above is the sprite sheet -->
<div id="wrapper">
<svg style="height: 100%;width:100%">
<use xlink:href="#icon-ellipsis"></use>
</svg>
</div>
<button id="btn">Jiggle :)</button>
</body>
CSS
#wrapper {
height: 48px;
width: 48px;
}
#div {
width: 50px
height: 50px
background-color: lightcoral;
}
JavaScript
const jiggle = () => {
const dots = (document.querySelectorAll("#icon-ellipsis > path.icon-ellipsis-dot"))
let delay = 100;
[].forEach.call(dots, (dot, i) => {
jsRequestAnimationFrame(delay, dot, () => {
jsRequestAnimationFrame(delay, dot, () => {
return;
}, true);
}, false);
delay = delay + 75;
});
}
const jsRequestAnimationFrame = (timeout, element, cb, mode) => {
let firstLoad = true;
let newTimeout;
const tick = (time) => {
if(firstLoad) {
newTimeout = timeout + time;
firstLoad = false;
}
// if time >= newTimeout we're done looping, time to run the callback and leave.
if(time >= newTimeout) return cb();
const calculatePixels = () => {
if(mode) return 4 - (4 * (1 - (newTimeout - time) / timeout));
return 4 * (1 - (newTimeout - time) / timeout);
};
element.style.transform = `translateY(-${(calculatePixels())}px)`;
requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
};
document.getElementById("btn").addEventListener("click", jiggle)
jiggle();