javascriptanimationsvgspecificationssmil

SVG <animate> rewind works in Firefox, not in Chrome


Here's a snippet where the user controls the progress of SVG <animate> using a slider. It works in Firefox. But in Chrome, after sliding it to the very end, sliding anywhere makes it go to the first frame for some reason.

Maybe Chrome stops the animation (and thus disables rewinding) once setCurrentTime() reaches the scheduled ending time of the animation. That would sound logical. But then why jump to the first frame (instead of staying at the last frame as fill="freeze" indicates) when slight rewind is attempted.

const input = document.querySelector('input');
const animate = document.querySelector('animate');
const svg = document.querySelector('svg');

animate.beginElement();
svg.pauseAnimations();
const duration = animate.getSimpleDuration();

input.addEventListener('input', function() {
  svg.setCurrentTime(this.value / 100 * duration);
});
<svg>
  <path id="my-path" d="M 10 10 L 40 10 L 40 40 L 10 40">
    <animate attributeName="d" to="M 20 5 L 10 50 L 70 10 L 10 20" dur="1s" begin="indefinite" fill="freeze"/>
  </path>
</svg>

<input type="range" value="0">

The problem can be solved by removing begin="indefinite". But that may limit other use cases or require hacky workarounds. (Just some examples: Maybe we want to use (user-supplied) SVG files without entering the slippery slope of making, keeping track of, and reverting (potentially conflicting!) changes to them. Also, removing begin="indefinite" causes animations to start playing "uncontrollably", which we may not want, so we'd have to keep track of all newly appearing SVG elements globally and hack them, and this might break the functionality of some third-party libraries that don't expect such modifications.)

What are other solutions? For example, I tried calling animate.beginElement() and svg.pauseAnimations() again after input.value went to 100 and back, but that shifted the progress of the animation.

And what's causing the problem, is it in accordance to spec or likely a bug in Chrome?

Here's a possibly weirder, non-interactive example. Its results on SO snippets in Chrome look 99% of the time like in Firefox. But rarely in SO snippets and every time I tried as a standalone HTML file (with JS code appended in a <script> tag), they look different from Firefox.

const input = document.querySelector('input');
const animate = document.querySelector('animate');
const svg = document.querySelector('svg');

animate.beginElement();
svg.pauseAnimations();
svg.setCurrentTime(1);
requestAnimationFrame(() => {
  svg.setCurrentTime(0.5);
});
<svg>
  <path id="my-path" d="M 10 10 L 40 10 L 40 40 L 10 40">
    <animate attributeName="d" to="M 20 5 L 10 50 L 70 10 L 10 20" dur="1s" begin="indefinite" fill="freeze"/>
  </path>
</svg>


Solution

  • A workaround is to set begin to target another animation that will never start.

    const input = document.querySelector('input');
    const animate = document.querySelector('animate');
    const svg = document.querySelector('svg');
    
    animate.beginElement();
    svg.pauseAnimations();
    const duration = animate.getSimpleDuration();
    input.addEventListener('input', function() {
      svg.setCurrentTime(this.value / 100 * duration);
    });
    <svg>
      <path id="my-path" d="M 10 10 L 40 10 L 40 40 L 10 40">
        <animate begin="other.begin" attributeName="d" to="M 20 5 L 10 50 L 70 10 L 10 20" dur="1s" fill="freeze"/>
        <animate id="other"/>
      </path>
    </svg>
    
    <input type="range" value="0">