Shape Morph
One SVG shape melts smoothly into the next, and the next, round and round, changing colour as it goes. Use it for logos, icons or playful illustrations.
How to use it
- Paste the snippet just before
</head>. - Put several
<path>s in one SVG and adddata-shape-morphto the SVG. The first path is shown and morphs into each of the others in turn.
Options
Add any of these attributes to the same element.
data-duration | Time for each morph. Default 900 ms. |
data-hold | How long each shape stays. Default 900 ms. |
fill="…" | On each path: the colour to change to when morphing into it. |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Shape Morph: one SVG shape melts smoothly into the next, and the next,
// round and round, colour included.
// Usage: put several <path>s in one SVG. The first is shown; it morphs into
// each of the others in turn.
// <svg data-shape-morph viewBox="0 0 100 100">
// <path d="…circle…" fill="#a855f7"/>
// <path d="…star…" fill="#ec4899"/>
// </svg>
// Optional: data-duration (ms per morph), data-hold (ms on each shape).
var DEFAULTS = { duration: 900, hold: 900 };
function run(svg) {
var d = svg.dataset;
var paths = Array.from(svg.querySelectorAll('path'));
if (paths.length < 2) return;
var shape = paths[0];
var targets = paths.slice(1).concat([paths[0].cloneNode()]);
targets.forEach(function (t) { t.style.display = 'none'; if (!t.parentNode) svg.appendChild(t); });
var i = 0;
(function next() {
var target = targets[i];
var props = {
d: anime.svg.morphTo(target),
duration: +d.duration || DEFAULTS.duration,
delay: +d.hold || DEFAULTS.hold,
ease: 'inOut(3)',
onComplete: function () { i = (i + 1) % targets.length; next(); }
};
var fill = target.getAttribute('fill');
if (fill) props.fill = fill;
anime.animate(shape, props);
})();
}
function init() {
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (!e.isIntersecting) return;
io.unobserve(e.target);
run(e.target);
});
}, { threshold: 0.3 });
document.querySelectorAll('[data-shape-morph]').forEach(function (svg) {
if (svg._sm) return;
svg._sm = true;
// Only the first shape is visible; the rest are morph targets.
svg.querySelectorAll('path').forEach(function (p, i) { if (i) p.style.display = 'none'; });
if (!reduced) io.observe(svg);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><svg data-shape-morph viewBox="0 0 100 100">
<path d="M50 10 C72 10 90 28 90 50 C90 72 72 90 50 90 C28 90 10 72 10 50 C10 28 28 10 50 10 Z" fill="#a855f7"/>
<path d="M50 6 L62 37 L95 38 L69 58 L78 91 L50 72 L22 91 L31 58 L5 38 L38 37 Z" fill="#facc15"/>
</svg>