<div></div>
body {
background-color: #333;
}
div {
height: 100px;
width: 100px;
border-radius: 50px;
background: #f0f;
}
// https://blog.codepen.io/2016/06/08/can-adjust-infinite-loop-protection-timing/
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 6000;
const { style, offsetWidth, offsetHeight } = document.querySelector("div");
const tweenable = new shifty.Tweenable(
{
scale: 1,
color: "#0f0",
x: 100,
y: 100
},
{
render: ({ x, y, scale, color }) => {
style.transform = `translate(${x}px, ${y}px) scale(${scale})`;
style.background = color;
}
}
);
const tween = tweenable.tween.bind(tweenable);
const reposition = async () => {
while (true) {
try {
let { target } = tweenable.data();
await tween({
to: { ...target, scale: 0.5, color: "#f0f" },
easing: "bouncePast",
duration: 750
});
await tween({
delay: 200,
to: { scale: 1, color: "#0f0" },
easing: "easeOutQuad",
duration: 750
});
// Repositioning is complete, so bail out of the loop.
break;
} catch (e) {
// This catch block is a no-op. From here the reposition
// while loop will simply restart and `try` again.
}
}
};
(async () => {
while (true) {
try {
await tween({
delay: 200,
to: {
scale: 0.75,
color: "#f0f"
},
duration: 1200,
easing: "easeOutQuad"
});
await tween({
delay: 100,
to: {
scale: 1,
color: "#0f0"
}
});
} catch (e) {
await reposition();
}
}
})();
document.addEventListener("click", (e) => {
tweenable.data({
target: { x: e.pageX - offsetWidth / 2, y: e.pageY - offsetHeight / 2 }
});
// Causes a rejection of the tween promise, which interrupts the animation and
// brings the control flow to the `catch` block of the currently running tween.
tweenable.cancel();
});
This Pen doesn't use any external CSS resources.