Fade-up on Scroll
Elements fade in and lift into place as they scroll into view. Anything that arrives together is staggered, so lists and grids cascade in order.
Scroll inside ↓
How to use it
- Paste the snippet just before
</head>. - Add
data-fade-up-on-scrollto each element that should animate in: cards, list items, sections.
Options
Add any of these attributes to the same element.
data-repeat | Animate again every time it comes back into view. |
data-distance | How far it rises, in px. Default 32. |
data-duration | How long it takes. Default 900 ms. |
data-stagger | Gap between items that arrive together. Default 90 ms. |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Fade-up on scroll: elements fade in and lift into place as they scroll into
// view. Elements that arrive together are staggered, so lists and grids cascade.
// Usage: <div data-fade-up-on-scroll>…</div> (each element you want to animate)
// Optional: data-distance (px, default 32), data-duration (ms), data-stagger
// (ms between items arriving together), data-repeat (animate again every time
// it re-enters the view).
var DEFAULTS = { distance: 32, duration: 900, stagger: 90 };
function hide(el) {
var dist = +el.dataset.distance || DEFAULTS.distance;
anime.utils.set(el, { opacity: 0, y: dist, scale: 0.98 });
}
function init() {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var els = document.querySelectorAll('[data-fade-up-on-scroll]');
var io = new IntersectionObserver(function (entries) {
var batch = [];
entries.forEach(function (e) {
var el = e.target;
var repeat = el.dataset.repeat !== undefined && el.dataset.repeat !== 'false';
if (e.isIntersecting) {
if (el._fuShown) return;
el._fuShown = true;
batch.push(el);
if (!repeat) io.unobserve(el);
} else if (repeat && el._fuShown && e.boundingClientRect.top > 0) {
// Left through the bottom: hide again so it replays on the way back.
el._fuShown = false;
hide(el);
}
});
// Sort by position so a cascade always runs top-left to bottom-right.
batch.sort(function (a, b) {
var ra = a.getBoundingClientRect(), rb = b.getBoundingClientRect();
return ra.top - rb.top || ra.left - rb.left;
});
batch.forEach(function (el, i) {
anime.animate(el, {
opacity: 1,
y: 0,
scale: 1,
duration: +el.dataset.duration || DEFAULTS.duration,
delay: i * (+el.dataset.stagger || DEFAULTS.stagger),
ease: 'out(3)'
});
});
}, { threshold: 0.15, rootMargin: '0px 0px -8% 0px' });
els.forEach(function (el) {
if (el._fuInit) return;
el._fuInit = true;
hide(el);
io.observe(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><div class="card" data-fade-up-on-scroll>…</div>
<div class="card" data-fade-up-on-scroll>…</div>
<div class="card" data-fade-up-on-scroll>…</div>