Isometric Assembly
An isometric illustration builds itself piece by piece: the ground, then the tree, the house, its door and windows, the roof and the chimney, each dropping into place with a little bounce. Loop it and the pieces lift away in reverse before it builds again.
How to use it
- Paste the snippet just before
</head>. - Draw your isometric picture as an SVG, one
<g>per piece, in the order they should arrive (back to front). - Add
data-isometric-assemblyto the SVG anddata-pieceto each piece.
Options
Add any of these attributes to the same element.
data-loop | Lift the pieces off in reverse, then build it again. |
data-stagger | Gap between pieces. Default 260 ms. |
data-duration | How long each piece takes to land. Default 750 ms. |
data-drop | How far each piece falls, in px. Default 60. |
data-hold | How long the finished picture stays before looping. Default 2200 ms. |
Without data-piece, the SVG’s direct children are the pieces. It starts when scrolled into view; with reduced motion the picture is simply there.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Isometric Assembly: an isometric illustration builds itself piece by
// piece, each part dropping into place with a little bounce, in the order
// the pieces appear in your markup.
// Usage:
// <svg data-isometric-assembly viewBox="…">
// <g data-piece>…</g> <!-- the floor -->
// <g data-piece>…</g> <!-- the walls -->
// <g data-piece>…</g> <!-- the roof -->
// </svg>
// Any element works too: its [data-piece] children are the parts. Without
// [data-piece], the direct children of the element (or of its first <svg>)
// are used.
// Optional: data-loop (lift the pieces off in reverse and build it again),
// data-stagger (ms between pieces, default 260), data-duration (ms each
// piece takes to land, default 750), data-drop (how far each piece falls,
// px, default 60), data-hold (ms the finished picture stays, default 2200).
var DEFAULTS = { stagger: 260, duration: 750, drop: 60, hold: 2200 };
function reduced() { return window.matchMedia('(prefers-reduced-motion: reduce)').matches; }
function pieces(el) {
var list = el.querySelectorAll('[data-piece]');
if (list.length) return Array.from(list);
var svg = el.tagName.toLowerCase() === 'svg' ? el : el.querySelector('svg');
return Array.from((svg || el).children).filter(function (c) {
return !/^(defs|title|desc|style)$/i.test(c.tagName);
});
}
function setup(el) {
var parts = pieces(el);
if (!parts.length) return;
el._iasParts = parts;
anime.utils.set(parts, { opacity: 0 });
}
function play(el) {
var d = el.dataset, parts = el._iasParts;
var stagger = d.stagger !== undefined ? +d.stagger : DEFAULTS.stagger;
var duration = +d.duration || DEFAULTS.duration;
var drop = d.drop !== undefined ? +d.drop : DEFAULTS.drop;
var hold = d.hold !== undefined ? +d.hold : DEFAULTS.hold;
var loop = d.loop !== undefined && d.loop !== 'false';
var tl = anime.createTimeline({ loop: loop, loopDelay: 400 });
tl.add(parts, {
y: { from: -drop, to: 0 },
opacity: { to: 1, duration: duration * 0.35, ease: 'linear' },
duration: duration,
ease: 'outBack(2)',
delay: anime.stagger(stagger)
});
if (loop) {
tl.add(parts, {
y: -drop * 0.6,
opacity: 0,
duration: 420,
ease: 'in(3)',
delay: anime.stagger(Math.max(40, stagger * 0.3), { from: 'last' })
}, '+=' + hold);
}
}
function init() {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (!e.isIntersecting) return;
io.unobserve(e.target);
play(e.target);
});
}, { threshold: 0.2 });
document.querySelectorAll('[data-isometric-assembly]').forEach(function (el) {
if (el._ias) return;
el._ias = true;
if (reduced()) return; // the finished picture, as drawn
setup(el);
if (el._iasParts) io.observe(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><svg data-isometric-assembly data-loop viewBox="0 0 400 300">
<g data-piece><!-- the ground --></g>
<g data-piece><!-- the walls --></g>
<g data-piece><!-- the roof --></g>
</svg>