Let’s begin with the simplest scenario
This article introduces the View Transitions API, which enables animations similar to Keynote’s “Magic Move” effect. It allows the browser to automatically detect differences between Scene 1 and Scene 2 and create animations to highlight these changes. This API is particularly well-suited for complex, page-level scene transition animations.
Add a fade-out animation to the image.
To insert an image into a page with animation using traditional methods, you would define it with CSS3 animations. For example:
img {
animate: fadeIn .2s both;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
container.append(img);
The implementation with the View Transitions API follows a different approach and requires just JavaScript code, as shown here:
document.startViewTransition(() => {
container.append(img);
});
After implementing this, the image insertion will display a fade-out effect, as shown in the GIF below.
change the animation duration and type settings
Although fading out the old element and fading in the new one is the most common animation effect, there are scenarios where you need to customize the animation’s duration and type.
For example, if we want the image to fade in within 1 second, or if we want the image to be appended to the page with a zoom effect, how do we handle that?
To achieve this, we need to use animation CSS properties along with a few CSS pseudo-elements specifically designed for the View Transitions API.
For instance, to set the animation duration to 1 second, you can do it like this:
::view-transition-group(root) {
animation-duration: 1s;
}
or
::view-transition-image-pair(root) {
animation-duration: 1s;
}
or
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 1s;
}
The four CSS pseudo-elements mentioned earlier may be confusing for many people. Let me explain this more clearly.
The View animation works by comparing the differences between the old and new snapshots, then animating them.
To simplify setting animation properties for these snapshots, several new CSS pseudo-elements were designed. There are a total of five, which together form a pseudo-element tree with the following hierarchical structure:
::view-transition
├─ ::view-transition-group(root)
│ └─ ::view-transition-image-pair(root)
│ ├─ ::view-transition-old(root)
│ └─ ::view-transition-new(root)
The root
is considered the root element, and this name is customizable, as explained later.
The most specific pseudo-elements for animation are ::view-transition-old
and ::view-transition-new
, which is why they are the most commonly used. The parent pseudo-elements are used to set common CSS properties, such as the animation duration mentioned above.
Setting the Animation Type
To change the animation type, for example, from a fade-in to a zoom effect, you might use the following code:
::view-transition-new(root) {
animation: scaleUp 1s;
}
@keyframes scaleUp {
from {
transform: scale(0.1);
}
to {
transform: scale(1);
}
}
However, after the image is inserted into the page, instead of the image zooming in, the entire content of the viewport zooms in.
How should this be handled? This is where the view-transition-name
CSS property is used.
view-transition-name And Local Animation Effects
We can use the view-transition-name
property to name any element. We can then use this name as a parameter to limit the scope of ::view-transition-old
and ::view-transition-new
.
For example:
img {
view-transition-name: wooo;
}
::view-transition-new(wooo) {
animation: scaleUp 1s;
}
@keyframes scaleUp {
from {
transform: scale(0.1);
}
to {
transform: scale(1);
}
}
Now, when the image is inserted into the page, you’ll see the zoom-in effect.
It is important to note that a name specified by view-transition-name can only apply to one element at a time, or else an error will occur, as illustrated in the error message below.
Therefore, if there are multiple animation elements, it’s safer to use JavaScript to assign a view-transition-name to each element, as shown in the following example:
img.style.viewTransitionName = 'wooo';
document.startViewTransition(() => {
this.after(img);
setTimeout(() => {
img.style.viewTransitionName = '';
}, 1000);
});
In this way, each image that is appended can have a zoom-in effect.
Complex Animation Becomes Much Simpler
The animation of a single image does not showcase the full power of view transitions. Below, I will demonstrate what the View Transitions API excels at and is recommended for.
Example 1: Reversing and Moving a List
In the past, when changing the order of a list, I always wanted to have an animation effect so users could visually see the change process.
In practice, it was too complicated, unless all the elements were absolutely positioned and the left and top values for each element were dynamically adjusted.
Now, with the View Transitions API, things are much simpler. With this view animation, you don’t need to set specific positioning values for elements. Instead, the animation effect is achieved based on the positional changes between the old and new snapshots.
So, it becomes:
- Assign a unique view-transition-name to all list elements.
- Call startViewTransition() and then change the position.
- Once done, that’s it—just these two steps. It’s very simple.
Whether the reverse order is achieved by changing the DOM position or by using CSS properties to visually reverse the order, both can have animation effects.
As shown in the GIF recording below:
And here is code:
ul {
display: flex;
max-width: 600px;
gap: 8px;
margin: 0;
padding: 0;
}
ul.reverse {
flex-direction: row-reverse;
}
li {
aspect-ratio: 1;
flex: 1;
display: grid;
place-items: center;
color: #fff;
background-color: deepskyblue;
}
const mytest = () => {
document.startViewTransition(() => {
[...ul.children].reverse().forEach(child => {
ul.append(child);
});
ul2.classList.toggle('reverse');
});
}
<h4 class="fill">Change DOM</h4>
<ul id="ul">
<li style="view-transition-name: li-1">1</li>
<li style="view-transition-name: li-2">2</li>
<li style="view-transition-name: li-3">3</li>
<li style="view-transition-name: li-4">4</li>
</ul>
<h4 class="fill">Change CSS</h4>
<ul id="ul2">
<li style="view-transition-name: li-5">1</li>
<li style="view-transition-name: li-6">2</li>
<li style="view-transition-name: li-7">3</li>
<li style="view-transition-name: li-8">4</li>
</ul>
<button id="button" onclick="mytest()">click me</button>
The key to the implementation is giving each list item a unique view transition name. In this way, the browser creates an independent animation group for each element, and the animations occur not at the page level but between the elements.
Example 2: DOM Element Fade-Out Removal
In the past, deleting a list item using the DOM.remove() method was very abrupt—just like a sudden “bang,” and the item disappeared. If you weren’t paying attention, you might not even notice which item was removed.
jQuery’s fadeOut() method was quite useful for this, as it would fade out the element before hiding it.
But with data-driven updates now, when the data changes, the list immediately changes. It’s so abrupt that you can’t use fadeOut() to smooth the transition.
Now, with the View Transitions API, it’s all simple. Whether the data changes or you manually remove the DOM, just call startViewTransition() and you’ll see the element fade out as it is removed, making the process straightforward.
The implementation is also very simple:
- First, assign a unique view-transition-name to the list elements.
- Second, execute JS code like the example below.
document.startViewTransition(() => {
dom.remove();
});
This results in the deletion animation shown below, simple, efficient, and visually effective.
Using @view-transition for Animations Between Pages
The View Transitions API can also be applied to multi-page navigation. To implement this, follow these steps:
Add the following CSS to your page:
@view-transition {
navigation: auto;
}
Next, define the animation type for the page transitions, as shown below:
@keyframes move-out {
from {
transform: translateX(0%);
}
to {
transform: translateX(-100%);
}
}
@keyframes move-in {
from {
transform: translateX(100%);
}
to {
transform: translateX(0%);
}
}
/* Apply animation effects to old and new snapshots during the view transition */
::view-transition-old(root) {
animation: 0.4s ease-in both move-out;
}
::view-transition-new(root) {
animation: 0.4s ease-in both move-in;
}
At this point, the page transition will have a sliding effect, similar to a single-page application.
Additional Explanations
The View Transitions API also includes two event methods: pagereveal
and pageswap
.
The pagereveal event is triggered when a document is initially displayed, whether it’s newly loaded from the web or activated from the back/forward cache (bfcache) or a pre-rendered state. The pageswap event is triggered when a document is about to be unloaded due to navigation.
These events can be used to implement custom behaviors.
Alright, see you in the next blog post. Bye!