Check out Angular’s support for the View Transitions API | by Andrew Scott | Dec, 2023


Two colorful web pages representing a transition
Two colorful web pages representing a transition

When a user navigates from one route to another, the Angular Router maps the URL path to the relevant component and displays its view. Animating this route transition can greatly enhance the user experience. In version 17, the Router now has support for the View Transitions API when navigating between routes in Chrome/Chromium browsers.

The native browser method that’s used for view transitions is document.startViewTransition. When startViewTransition() is called, the browser captures the current state of the page which includes taking a screenshot. The method takes a callback that updates the DOM and this function can be asynchronous. The new state is captured and the transition begins in the next animation frame when the promise returned by the callback resolves.

Here’s an example of the startViewTransition api:

document.startViewTransition(async () => {
await updateTheDOMSomehow();
});

If you’re curious to read more about the details of the browser API, the Chrome Explainer is an invaluable resource.

Several things happen after navigation starts in the router: route matching, loading lazy routes and components, executing guards and resolvers to name a few. Once these have completed successfully, the new routes are ready to be activated. This route activation is the DOM update that we want to perform as part of the view transition.

When the view transition feature is enabled, navigation “pauses” and a call is made to the browser’s startViewTransition method. Once the startViewTransition callback executes (this happens asynchronously, as outlined in the spec here), navigation “resumes”. The remaining steps for the router navigation include updating the browser URL and activating or deactivating the matched routes (the DOM update).

Finally, the callback passed to startViewTransition returns a Promise that resolves once Angular has finished rendering. As described above, this indicates to the browser that the new DOM state should be captured and the transition should begin.

View transitions are a progressive enhancement. If the browser does not support the API, the Router will perform the DOM updates without calling startViewTransition and the navigation will not be animated.

To enable this feature add withViewTransitions to the provideRouter function:

bootstrapApplication(MyApp, {providers: [
provideRouter(routes, withViewTransitions()),
]});

Try the “count” example on StackBlitz

This example uses the counter application from the Chrome explainer and replaces the direct call to startViewTransition when the counter increments with a router navigation.

View transitions can be customized with CSS. We can also instruct the browser to create separate elements for the transition by setting a view-transition-name. We can expand the first example by adding view-transition-name: count to the .count style in the Counter component. Then, in the global styles, we can define a custom animation for this view transition:

/* Custom transition */
@keyframes rotate-out {
to {
transform: rotate(90deg);
}
}
@keyframes rotate-in {
from {
transform: rotate(-90deg);
}
}
::view-transition-old(count),
::view-transition-new(count) {
animation-duration: 200ms;
animation-name: -ua-view-transition-fade-in, rotate-in;
}
::view-transition-old(count) {
animation-name: -ua-view-transition-fade-out, rotate-out;
}

It is important that the view transition animations are defined in a global style file. They cannot be defined in the component styles because the default view encapsulation will scope the styles to the component.

Try the updated “count” example on StackBlitz

The withViewTransitions router feature can also be called with an options object that includes an onViewTransitionCreated callback. This callback is run in an injection context and receives a ViewTransitionInfo object that includes the ViewTransition returned from startViewTransition, as well as the ActivatedRouteSnapshot that the navigation is transitioning from and the new one that it is transitioning to.

This callback can be used for any number of customizations. For example, you might want to skip transitions under certain conditions. We use this on the new angular.dev docs site:

withViewTransitions({
onViewTransitionCreated: ({transition}) => {
const router = inject(Router);
const targetUrl = router.getCurrentNavigation()!.finalUrl!;
// Skip the transition if the only thing
// changing is the fragment and queryParams
const config = {
paths: 'exact',
matrixParams: 'exact',
fragment: 'ignored',
queryParams: 'ignored',
};

if (router.isActive(targetUrl, config)) {
transition.skipTransition();
}
},
}),

In this code snippet, we create a UrlTree from the ActivatedRouteSnapshot the navigation is going to. We then check with the Router to see if this UrlTree is already active, ignoring any differences in the fragment or query parameters. If it is already active, we call skipTransition which will skip the animation portion of the view transition. This is the case when clicking on an anchor link that will only scroll to another location in the same document.

We’ve recreated some of the great examples from the Chrome Team in Angular for you to explore.

Transitioning elements don’t need to be the same DOM element

Custom entry and exit animations

Async DOM updates and waiting for content

During this time, the page is frozen, so delays here should be kept to a minimum…in some cases it’s better to avoid the delay altogether, and use the content you already have.

The view transition feature in the Angular router does not provide a way to delay the animation. For the moment, our stance is that it’s always better to use the content you have rather than making the page non-interactive for any additional amount of time.

Changing the transition depending on the type of navigation

Transitioning without freezing other animations

Animating with Javascript

While Angular’s support for the View Transitions API is still experimental, we’re excited for the possibilities it creates to build the next generation of web experiences for users. The team will continue working to stabilize support and you can help. Try out this feature and leave feedback on GitHub. We’ve included multiple examples for you to try out and have fun with. If you implement something cool, tag us on X and show us the cool things you create.

Until the next time thanks for being a part of the Angular community and keep building.



Source link

2023. All Rights Reserved.