How to start an animated SVG on entering viewport?

To trigger an animation when the SVG scrolls into view, there are two steps you need to resolve:

  1. detect when the SVG is visible
  2. have the animation paused initially, then trigger it when you want

Detecting when an element is visible

You need to add an event handler for scroll events. In that event handler, you compare the page scroll location (window.scrollY) against the SVG’s location on the page. You can get that using getBoundingClientRect().

See the demos below for example code implementing this technique.

Starting an animation running

The second step is actually triggering the animation at that time. How you keep an animation paused and then start it running, depends on the method of animation you are using.

SVG SMIL animation

In this first example, we are using the SMIL animation elements that are part of the SVG standard.

To have an animation wait for you to start it running, set the begin attribute to "indefinite". The you can start it running by calling beginElement() on the <animate> element.

// Get the position on the page of the SVG
var svgLocation = document.getElementById("mysvg").getBoundingClientRect();

// Scroll offset that triggers animation start.
// In this case it is the bottom of the SVG.
var offsetToTriggerAnimation = svgLocation.y + svgLocation.height;

// Function to handle the scroll event.
// Add an event handler to the document for the "onscroll" event
function scrollAnimTriggerCheck(evt)
{
  var viewBottom = window.scrollY + window.innerHeight;
  if (viewBottom > offsetToTriggerAnimation) {
    // Start the SMIL animation
    document.getElementById("anim").beginElement();
    // Remove the event handler so it doesn't trigger again
    document.removeEventListener("scroll", scrollAnimTriggerCheck);
  }
}

// Add an event handler to the document for the "onscroll" event
document.addEventListener("scroll", scrollAnimTriggerCheck);
svg {
  border: solid 1px grey;
}

p {
  margin-bottom: 1000px;
}
<p>Scroll down</p>

<svg id="mysvg">
  <rect x="0" width="150" height="150" fill="rebeccapurple">
    <animate id="anim" attributeName="x" from="0"  to="150" dur="2s"
             fill="freeze" begin="indefinite"/>
  </rect>
</svg>

<br>
<br>
<br>
<br>

CSS animation

There are a couple of way you can trigger an animation to start when you are using CSS animations.

  1. Put your animation property in its own style set class, then add that class to the element:

     .anim {
       animation-name: slide;
       animation-duration: 2s;
     }
    
     myobject.classList.add("anim");
    
// Get the position on the page of the SVG
var svgLocation = document.getElementById("mysvg").getBoundingClientRect();

// Scroll offset that triggers animation start.
// In this case it is the bottom of the SVG.
var offsetToTriggerAnimation = svgLocation.y + svgLocation.height;

// Function to handle the scroll event.
// Add an event handler to the document for the "onscroll" event
function scrollAnimTriggerCheck(evt)
{
  var viewBottom = window.scrollY + window.innerHeight;
  if (viewBottom > offsetToTriggerAnimation) {
    // Start the CSS animation by adding the animation CSS rules to the rect element
    document.querySelector("rect").classList.add("anim");;
    // Remove the event handler so it doesn't trigger again
    document.removeEventListener("scroll", scrollAnimTriggerCheck);
  }
}

// Add an event handler to the document for the "onscroll" event
document.addEventListener("scroll", scrollAnimTriggerCheck);
.anim {
  animation: slide 2s linear forwards;
}

@keyframes slide {
  from {
    transform: translate(0px, 0px);
  }

  to {
    transform: translate(150px, 0px);
  }
}

svg { border: solid 1px grey; }
p { margin-bottom: 1000px; }
<p>Scroll down</p>

<svg id="mysvg">
  <rect x="0" width="150" height="150" fill="rebeccapurple"/>
</svg>

<br>
<br>
<br>
<br>
  1. The second way is to make use of the animation-play-state CSS property. Not all browsers support this yet.

    The idea is that you set animation-play-state to paused initially, then change it to running when you want the animation to start. You can either do that by adding a class with that property (similar to above). Or you can set the style.animationPlayState property directly, as in the example below.

// Get the position on the page of the SVG
var svgLocation = document.getElementById("mysvg").getBoundingClientRect();

// Scroll offset that triggers animation start.
// In this case it is the bottom of the SVG.
var offsetToTriggerAnimation = svgLocation.y + svgLocation.height;

// Function to handle the scroll event.
// Add an event handler to the document for the "onscroll" event
function scrollAnimTriggerCheck(evt)
{
  var viewBottom = window.scrollY + window.innerHeight;
  if (viewBottom > offsetToTriggerAnimation) {
    // Start the CSS animation by setting the animation-play-state to "running"
    document.querySelector("rect").style.animationPlayState = "running";
    // Remove the event handler so it doesn't trigger again
    document.removeEventListener("scroll", scrollAnimTriggerCheck);
  }
}

// Add an event handler to the document for the "onscroll" event
document.addEventListener("scroll", scrollAnimTriggerCheck);
.anim {
  animation: slide 2s linear forwards;
  animation-play-state: paused;
}

@keyframes slide {
  from {
    transform: translate(0px, 0px);
  }

  to {
    transform: translate(150px, 0px);
  }
}

svg { border: solid 1px grey; }
p { margin-bottom: 1000px; }
<p>Scroll down</p>

<svg id="mysvg">
  <rect x="0" width="150" height="150" fill="rebeccapurple" class="anim"/>
</svg>

<br>
<br>
<br>
<br>

Leave a Comment