javascriptcsssvgview-transitions-api

Smoothly transition SVG elements with the view transition API


I have the following problem: I am trying to move SVG elements with the view transition API smoothly from one position to the other by applying a view-transition-name. While this works perfectly for HTML elements for some reason only the default cross-fade animation is applied to SVG elements.

In the demo you see two elements: a circle rendered with SVG and a square rendered with HTML. When the SVG circle is clicked the default animation, the cross-fade, is applied. When the HTML square is clicked a smooth transition is performed. Both examples have more or less the same config: startViewTransition is called, the element is replaced with the same ID as before. Shouldn't that smooth transition also work for SVG elements, am I doing something wrong in my demo or did I miss something?

I know that the smooth transition for SVGs can be achieved by customizing the animation with the web animations API for example, but I'd like to get that working with the view-transition-name only.

Unfortunately this demo only works in Chromium-based browsers at the moment.

Demo - also on CodeSandbox

document.getElementById("svg").addEventListener("click", () => {
  document.startViewTransition(() => {
    // Update DOM for the second state
    document.getElementById("svg").innerHTML = `
          <circle id="icon" style="transform: translate(100px, 100px);" r="40" fill="red" />
  `;
  });
});

document.getElementById("second").addEventListener("click", () => {
  document.startViewTransition(() => {
    document.getElementById(
      "second"
    ).innerHTML = `<div id="item" style="width: 200px; height: 200px; background-color: blue; position: absolute; transform: translate(200px, 200px);"></div>`;
  });
});
icon {
  view-transition-name: icon;
}

#second {
  width: 500px;
  height: 500px;
}

#item {
  background-color: green;
  width: 100px;
  height: 100px;
  view-transition-name: item;
}
<div id="first">
  <svg 
    id="svg" 
    viewBox="0 0 100 100" 
    style="width: 100px; height: 100px">
        <circle id="icon" cx="50" cy="50" r="40" fill="blue" />
  </svg>
</div>
<div id="second">
  <div id="item"></div>
</div>


Solution

  • When inspecting the transition with Chrome DevTools I can see there is no ::view-transition-group(icon). This means that the element is not captured as part of the snapshotting process.

    The reason for this is that the SVG element is Embedded Content. Because of that, the capturing process captures the SVG as a whole, without looking into its contents.

    The reason why you are seeing a cross-fade is because of the :root element that gets captured: the SVG is part of the root snapshot which is set by default through the User Agent StyleSheet.