Control
SVG Web Animation
with
Slider and Button


Here's how to animate an SVG using the Web Animations API and control the animation using a start/stop button as well as a slider.

SVG Animation Demo

Use the slider and the button to control the animation of the SVG below.



Status information goes here once you start the animation
I'm a link

Code

The SVG

First, we define the SVG within our HTML code:
<svg id="ellipse" width="30em" fill="CadetBlue" viewBox="-50 -50 100 100">
  <ellipse rx="40%" ry="30%"/>
  <style>.centeredText {font: bold 10px sans-serif; fill: RoyalBlue; text-anchor: middle; dominant-baseline: middle}</style>
  <a href="https://mb.sb" target="_blank"><text class="centeredText">I'm a link</text></a>
</svg>

The SVG contains two elements, an ellipse and a link. Since we want to rotate the ellipse around its center in this example, it's necessary to put the elements in the center of the SVG. One way to do this is by setting both the x-min and y-min parameters of the SVG viewBox to -50 which is half of the viewBox's width and height, 100.

Have look at this or this tutorial to learn more about SVG's viewBox.

Create the Animation

After the DOM, including our SVG, has loaded, we'll use JavaScript to create an animation for the SVG. There are various ways to make sure the JavaScript executes after the DOM is ready. I've simply put the script block after the body. Alternatively, you can use a script block in front of the body and defer its execution by declaring it a JavaScript module:
<script type="module">
Whichever way you choose, the next part creates an Animation object. When we run the animation, it will rotate the SVG 360 degrees and change its fill color.
const changeRotationAndColor = [ {
        transform: 'rotate(360deg)',
        fill: 'RebeccaPurple'
        },
    ];

const animationTiming = {
      duration: 3000,
      // When the animation is finished, don't reset the SVG to its initial state
      fill: "forwards",
      easing: 'ease-in-out'
    }

// Create the animation object but pause it immediately to prevent it from running on page load
const animation = document.getElementById('ellipse').animate(
      changeRotationAndColor,
      animationTiming
    );
animation.pause();

Add code to the button and the slider

The rest of the JavaScript code is responsible for changing the current progress of the animation we just created using a slider and a button.

The following JavaScript assumes an HTML input element of type range with ID "slider", a button "startStopBtn", and a div "statusInfo".

Once the user has pressed the start button to run the animation, we use setInterval to keep the slider in sync with the animation by polling the animation's progress repeatedly.

// Duration of the animation in milliseconds
const animationDuration = animation.effect.getComputedTiming().duration;

const slider = document.getElementById('slider');
// When reloading the page, we reset the slider value.
// Otherwise, it would keep its value before the reload
slider.value = slider.min;

const startStopBtn = document.getElementById('startStopBtn')

const statusInfo = document.getElementById('statusInfo')

// Identifies the function that moves the slider automatically after starting the animation
let sliderMoveIntervalId;

function moveSliderPeriodically() {
      sliderMoveIntervalId = setInterval(() => {
            setSliderProgressToAnimationProgress();
            statusInfo.innerHTML=`Animation running, moved slider to ${getAnimationProgress()}%`;
          }, 100);
    };

// Gets the animation progress in percent, as an integer
function getAnimationProgress() {
      return Math.trunc(animation.currentTime / animationDuration * 100);
    }

function setSliderProgressToAnimationProgress() {
      slider.value = getAnimationProgress() / 100 * slider.max;
    }

startStopBtn.addEventListener('click', (e) => {
      if(animation.playState === "running") {
            // Stop the automated progress of the slider when the user presses the stop button
            clearInterval(sliderMoveIntervalId);
            setSliderProgressToAnimationProgress();

            animation.pause();

            statusInfo.innerHTML = `Animation stopped at ${getAnimationProgress()}% with button`;
            startStopBtn.textContent = "Run animation";
          }
      else {
            moveSliderPeriodically();
            animation.play();
            startStopBtn.textContent = "Stop animation";
          }
    }
    );

// The user moved the slider → Update the animation
slider.addEventListener('input', (e) => {
      // Stop the automated progress of the slider if the animation's running
      if(animation.playState === "running") {
            clearInterval(sliderMoveIntervalId);
            startStopBtn.textContent = "Run animation";
          }

      // We pause the animation no matter the playState since if the animation is finished,
      // setting animation.currentTime would resume running the animation
      animation.pause();

      // Change the animation's percentage to the percentage of the slider
      const sliderPercentage = slider.value / slider.max * 100;
      animation.currentTime = animationDuration / 100 * sliderPercentage;
      statusInfo.innerHTML = `User moved slider, set animation to ${getAnimationProgress()}%`;
    });

animation.onfinish = () => {
      statusInfo.innerHTML= "Animation finished";
      clearInterval(sliderMoveIntervalId);
      // Set the slider in case the last interval hasn't run
      setSliderProgressToAnimationProgress();
      startStopBtn.textContent = "Rerun animation";
    }

Try it yourself

To inspect and play around with the full SVG and animation code on this page, press Ctrl+Shift+c. This will open the Inspector in Chrome or Firefox.