The ViewTransition
interface of the View Transitions API represents an active view transition, and provides functionality to react to the transition reaching different states (e.g. ready to run the animation, or animation finished) or skip the transition altogether.
This object type is made available in the following ways:
- In the case of same-document (SPA) transitions, it is returned by the
document.startViewTransition()
method. - In the case of cross-document (MPA) transitions, it is made available:
When a view transition is triggered by a startViewTransition()
call (or a page navigation in the case of MPA transitions), a sequence of steps is followed as explained in The view transition process. This also explains when the different promises fulfill.
In the following SPA example, the ViewTransition.ready
promise is used to trigger a custom circular reveal view transition emanating from the position of the user's cursor on click, with animation provided by the Web Animations API.
let lastClick;
addEventListener("click", (event) => (lastClick = event));
function spaNavigate(data) {
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
const x = lastClick?.clientX ?? innerWidth / 2;
const y = lastClick?.clientY ?? innerHeight / 2;
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y),
);
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
});
transition.ready.then(() => {
document.documentElement.animate(
{
clipPath: [
`circle(0 at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
],
},
{
duration: 500,
easing: "ease-in",
pseudoElement: "::view-transition-new(root)",
},
);
});
}
This animation also requires the following CSS, to turn off the default CSS animation and stop the old and new view states from blending in any way (the new state "wipes" right over the top of the old state, rather than transitioning in):
::view-transition-image-pair(root) {
isolation: auto;
}
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
display: block;
}