Press Squash
Press the button and it squashes down like soft rubber, wider and flatter, for as long as you hold it. Let go and it springs back, overshooting into a little stretch before it settles. Enter and Space work too.
How to use it
- Paste the snippet just before
</head>. - Add
data-press-squashto buttons or any clickable element.
Options
Add any of these attributes to the same element.
data-amount | How much it squashes, 0 to 1. Default 0.14. |
data-autoplay | It presses itself every couple of seconds while in view. Handy for demos. |
From code, animacious.squash(element) plays one press.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Press Squash: press a button and it squashes down like soft rubber, wider
// and flatter, for as long as you hold it. Let go and it springs back,
// overshooting into a stretch before it settles. Enter and Space work too.
// Usage: <button data-press-squash>Press me</button>
// Optional: data-amount (how much it squashes, 0–1, default 0.14),
// data-autoplay (it presses itself every couple of seconds while in view;
// handy for demos).
// Script use: animacious.squash(element) plays one press.
var DEFAULTS = { amount: 0.14, every: 2200 };
function amount(el) {
var a = parseFloat(el.dataset.amount);
return a >= 0 && a <= 1 ? a : DEFAULTS.amount;
}
function down(el) {
if (el._psqDown) return;
el._psqDown = true;
var a = amount(el);
anime.animate(el, {
scaleX: 1 + a * 0.75,
scaleY: 1 - a,
y: el.offsetHeight * a * 0.25,
duration: 160,
ease: 'out(3)'
});
}
function up(el) {
if (!el._psqDown) return;
el._psqDown = false;
// The elastic ease carries it past rest into a stretch, then back.
anime.animate(el, {
scaleX: 1,
scaleY: 1,
y: 0,
duration: 900,
ease: 'outElastic(1.2, .35)'
});
}
function squash(el) {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
down(el);
setTimeout(function () { up(el); }, 220);
}
function setup(el) {
el.style.transformOrigin = '50% 100%';
el.addEventListener('pointerdown', function (e) { if (e.button === 0) down(el); });
['pointerup', 'pointerleave', 'pointercancel'].forEach(function (t) {
el.addEventListener(t, function () { up(el); });
});
el.addEventListener('keydown', function (e) { if ((e.key === ' ' || e.key === 'Enter') && !e.repeat) down(el); });
el.addEventListener('keyup', function (e) { if (e.key === ' ' || e.key === 'Enter') up(el); });
el.addEventListener('blur', function () { up(el); });
if (el.dataset.autoplay !== undefined && el.dataset.autoplay !== 'false') {
var timer = null;
var press = function () {
if (!el.matches(':hover')) squash(el);
timer = setTimeout(press, DEFAULTS.every);
};
new IntersectionObserver(function (entries) {
clearTimeout(timer);
if (entries[0].isIntersecting) timer = setTimeout(press, 600);
}).observe(el);
}
}
function init() {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
document.querySelectorAll('[data-press-squash]').forEach(function (el) {
if (el._psq) return;
el._psq = true;
setup(el);
});
}
window.animacious = window.animacious || {};
window.animacious.squash = squash;
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><button data-press-squash>Add to cart</button>