Fire SVG (SMIL) animations when viewing SVG— zachleat.com

/
/
/
24 Views

There is a use case where the scheme has an SVG animation running in one mediation and only one. As it is (some content removed):

If you’re on a device with a small vertical viewport size, you probably haven’t noticed the animation at the height. Watch the animations start (and finish!) See or not SVG!

I want to wire the animations up to the IntersectionObserver to make sure they only start animating when visible. Here’s how I did it:

Edit the SVG

I have found all the examples of <animate> or <animateTransform> and worked the begin attributes to correctly cascade the sequence of animations inside. I wanted them all to start when my bezier curve started animating, so I added one id in that animation (there is an issue with dashes in that id, so watch out with dashes):

<animate
id="mysvgline"
attributeType="xml"
attributeName="stroke-dashoffset"
from="500"
to="0"
dur=".8s"
begin="0s"
/>

Learn how to move a line / curve: CSS Tricks: How SVG Line Animation works

Note the begin virtue of height, which may cause later.

Now I want to find other animations in my SVG that I want to start simultaneously and modify their begin virtue to use id from high with a .begin opposite It starts this animation when the reference animation starts. It looks like this:

<animateMotion
begin="mysvgline.begin"
dur=".8s"
repeatCount="1"
fill="freeze"
path="M35.5 20C216.281 20 352.411 182 512.5 182"
/>

Alternatively, you can use .end to start this animation when the said animation is finished.

(Side note: repeatCount="1" and fill="freeze" best barkadas. fill="freeze" means that your animation will not return to the first frame at the end)

Next go back to the original animation and edit begin virtue of indefinite (Read more on MDN: begin – SVG). It tells SVG that it won’t start until I can use JavaScript to enable it to be used .beginElement().

<animate
id="mysvgline"
attributeType="xml"
attributeName="stroke-dashoffset"
from="500"
to="0"
dur=".8s"
begin="indefinite"
/>

(Another note: just think loudly here as I write this-Maybe I can use <noscript> SVG content as a no-JS fallback)

Add IntersectionObserver

It can be seen:

if ('IntersectionObserver' in window)  {
let elements = document.querySelectorAll("svg");

let observer = new IntersectionObserver(entries => {
let mediaQuery = window.matchMedia('(prefers-reduced-motion: no-preference)');
if(!mediaQuery.matches) {
return;
}

for(let entry of entries) {
if(!entry.isIntersecting) {
continue;
}


let beginElements = entry.target.querySelectorAll(`:scope [begin="indefinite"]`);
for(let beginEl of beginElements) {
beginEl.beginElement();


observer.unobserve(entry.target);
}
}
},
{
threshold: .5
});

for(let elem of elements) {
observer.observe(elem);
}
}

Try it yourself

Leave a Comment

Your email address will not be published. Required fields are marked *

This div height required for enabling the sticky sidebar