Skip to content

Commit 48216a2

Browse files
atscottpkozlowski-opensource
authored andcommitted
refactor(router): Move view transitions to developer preview (angular#55600)
Nothing of concern arose during the time this feature was in the experimental phase. There are no plans to change the shape of the API. This change also updates the route animations documentation to use the view transitions feature instead of the old and difficult to follow guide that used the animations package. The content was taken from the blog post: https://blog.angular.io/check-out-angulars-support-for-the-view-transitions-api-3937376cfc19 PR Close angular#55600
1 parent 5a04837 commit 48216a2

File tree

3 files changed

+111
-98
lines changed

3 files changed

+111
-98
lines changed
-193 KB
Binary file not shown.
Lines changed: 110 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,144 +1,157 @@
11
# Route transition animations
22

3-
Routing enables users to navigate between different routes in an application.
3+
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. The Router has support for the View Transitions API when navigating between routes in Chrome/Chromium browsers.
44

5-
## Enable routing transition animation
5+
HELPFUL: The Router's native View Transitions integraiton is currently in [developer preview](/reference/releases#developer-preview). Native View Transitions are also a relatively new feature so there may be limited support in some browsers.
66

7-
When a user navigates from one route to another, the Angular router maps the URL path to a relevant component and displays its view.
8-
Animating this route transition can greatly enhance the user experience.
7+
## How View Transitions work
98

10-
The Angular router comes with high-level animation functions that let you animate the transitions between views when a route changes.
11-
To produce an animation sequence when switching between routes, you need to define nested animation sequences.
12-
Start with the top-level component that hosts the view, and nest animations in the components that host the embedded views.
9+
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.
1310

14-
To enable routing transition animation, do the following:
15-
16-
1. Create a routing configuration that defines the possible routes. For NgModule based applications, this will include creating a `RouterModule` and adding it to the main `AppModule`.
17-
1. Add a router outlet to tell the Angular router where to place the activated components in the DOM.
18-
1. Define the animation.
19-
20-
Illustrate a router transition animation by navigating between two routes, *Home* and *About* associated with the `HomeComponent` and `AboutComponent` views respectively.
21-
Both of these component views are children of the top-most view, hosted by `AppComponent`.
22-
Implement a router transition animation that slides in the new view to the right and slides out the old view when navigating between the two routes.
23-
24-
<img alt="Animations in action" width="440" src="assets/images/guide/animations/route-animation.gif">
25-
26-
## Route configuration
27-
28-
To begin, configure a set of routes. This route configuration tells the router how to navigate.
29-
30-
Create a `Routes` array to define a set of routes. Add the routes to the `provideRouter` function in the providers array of the `bootstrapApplication` function call in `main.ts`.
11+
Here’s an example of the startViewTransition api:
3112

3213
```ts
33-
bootstrapApplication(AppComponent, {
34-
providers: [
35-
provideRouter(appRoutes),
36-
]
14+
document.startViewTransition(async () => {
15+
await updateTheDOMSomehow();
3716
});
3817
```
3918

40-
Note: For `NgModule` based applications:
41-
Use the `RouterModule.forRoot` method to define a set of routes.
42-
Also, add `RouterModule` to the `imports` array of the main module, `AppModule`.
43-
44-
Use the `RouterModule.forRoot` method in the root module, `AppModule`, to register top-level application routes and providers.
45-
For feature modules, call the `RouterModule.forChild` method instead.
19+
If you’re curious to read more about the details of the browser API, the [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions) is an invaluable resource.
4620

47-
The following configuration defines the possible routes for the application.
21+
## How the Router uses view transitions
4822

49-
<docs-code header="src/app/app.routes.ts" path="adev/src/content/examples/animations/src/app/app.routes.ts" visibleRegion="route-animation-data"/>
23+
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.
5024

51-
The `home` and `about` paths are associated with the `HomeComponent` and `AboutComponent` views.
52-
The route configuration tells the Angular router to instantiate the `HomeComponent` and `AboutComponent` views when the navigation matches the corresponding path.
25+
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).
5326

54-
The `data` property of each route defines the key animation-specific configuration associated with a route.
55-
The `data` property value is passed into `AppComponent` when the route changes.
27+
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.
5628

57-
HELPFUL: The `data` property names that you use can be arbitrary.
58-
For example, the name *animation* used in the preceding example is an arbitrary choice.
29+
View transitions are a [progressive enhancement](https://developer.mozilla.org/en-US/docs/Glossary/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.
5930

60-
## Router outlet
31+
## Enabling View Transitions in the Router
6132

62-
After configuring the routes, add a `<router-outlet>` inside the root `AppComponent` template.
63-
The `<router-outlet>` directive tells the Angular router where to render the views when matched with a route.
33+
To enable this feature, simply add `withViewTransitions` to the `provideRouter` or set `enableViewTransitions: true` in `RouterModule.forRoot`:
6434

65-
The `ChildrenOutletContexts` holds information about outlets and activated routes.
66-
The `data` property of each `Route` can be used to animate routing transitions.
67-
68-
<docs-code header="src/app/app.component.html" path="adev/src/content/examples/animations/src/app/app.component.html" visibleRegion="route-animations-outlet"/>
35+
```ts
36+
// Standalone bootstrap
37+
bootstrapApplication(MyApp, {providers: [
38+
provideRouter(ROUTES, withViewTransitions()),
39+
]});
40+
41+
// NgModule bootstrap
42+
@NgModule({
43+
imports: [RouterModule.forRoot(routes, {enableViewTransitions: true})]
44+
})
45+
export class AppRouting {}
46+
```
6947

70-
`AppComponent` defines a method that can detect when a view changes.
71-
The method assigns an animation state value to the animation trigger \(`@routeAnimation`\) based on the route configuration `data` property value.
72-
Here's an example of an `AppComponent` method that detects when a route change happens.
48+
[Try the “count” example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-2dnvtm?file=src%2Fmain.ts)
49+
50+
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.
51+
52+
## Using CSS to customize transitions
53+
54+
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:
55+
56+
```css
57+
/* Custom transition */
58+
@keyframes rotate-out {
59+
to {
60+
transform: rotate(90deg);
61+
}
62+
}
63+
@keyframes rotate-in {
64+
from {
65+
transform: rotate(-90deg);
66+
}
67+
}
68+
::view-transition-old(count),
69+
::view-transition-new(count) {
70+
animation-duration: 200ms;
71+
animation-name: -ua-view-transition-fade-in, rotate-in;
72+
}
73+
::view-transition-old(count) {
74+
animation-name: -ua-view-transition-fade-out, rotate-out;
75+
}
76+
```
7377

74-
<docs-code header="src/app/app.component.ts" path="adev/src/content/examples/animations/src/app/app.component.ts" visibleRegion="get-route-animations-data"/>
78+
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.
7579

76-
The `getRouteAnimationData()` method takes the value of the outlet. It returns a string that represents the state of the animation based on the custom data of the current active route.
77-
Use this data to control which transition to run for each route.
80+
[Try the updated “count” example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-fwn4i7?file=src%2Fmain.ts)
7881

79-
## Animation definition
82+
## Controlling transitions with onViewTransitionCreated
8083

81-
Animations can be defined directly inside your components.
82-
For this example you are defining the animations in a separate file, which allows re-use of animations.
84+
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](/guide/di/dependency-injection-context#run-within-an-injection-context) and receives a [ViewTransitionInfo](/api/router/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.
8385

84-
The following code snippet defines a reusable animation named `slideInAnimation`.
86+
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:
8587

86-
<docs-code header="src/app/animations.ts" path="adev/src/content/examples/animations/src/app/animations.ts" visibleRegion="route-animations"/>
88+
```ts
89+
withViewTransitions({
90+
onViewTransitionCreated: ({transition}) => {
91+
const router = inject(Router);
92+
const targetUrl = router.getCurrentNavigation()!.finalUrl!;
93+
// Skip the transition if the only thing
94+
// changing is the fragment and queryParams
95+
const config = {
96+
paths: 'exact',
97+
matrixParams: 'exact',
98+
fragment: 'ignored',
99+
queryParams: 'ignored',
100+
};
101+
102+
if (router.isActive(targetUrl, config)) {
103+
transition.skipTransition();
104+
}
105+
},
106+
}),
107+
```
87108

88-
The animation definition performs the following tasks:
109+
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.
89110

90-
* Defines two transitions \(a single `trigger` can define multiple states and transitions\)
91-
* Adjusts the styles of the host and child views to control their relative positions during the transition
92-
* Uses `query()` to determine which child view is entering and which is leaving the host view
111+
## Examples from the Chrome explainer adapted to Angular
93112

94-
A route change activates the animation trigger, and a transition matching the state change is applied.
113+
We’ve recreated some of the great examples from the Chrome Team in Angular for you to explore.
95114

96-
HELPFUL: The transition states must match the `data` property value defined in the route configuration.
115+
### Transitioning elements don’t need to be the same DOM element
97116

98-
Make the animation definition available in your application by adding the reusable animation \(`slideInAnimation`\) to the `animations` metadata of the `AppComponent`.
117+
* [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#transitioning_elements_dont_need_to_be_the_same_dom_element)
118+
* [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-dh8npr?file=src%2Fmain.ts)
99119

100-
<docs-code header="src/app/app.component.ts" path="adev/src/content/examples/animations/src/app/app.component.ts" visibleRegion="define"/>
120+
### Custom entry and exit animations
101121

102-
### Style the host and child components
122+
* [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#custom_entry_and_exit_transitions)
123+
* [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-8kly3o)
103124

104-
During a transition, a new view is inserted directly after the old one and both elements appear on screen at the same time.
105-
To prevent this behavior, update the host view to use relative positioning.
106-
Then, update the removed and inserted child views to use absolute positioning.
107-
Adding these styles to the views animates the containers in place and prevents one view from affecting the position of the other on the page.
125+
### Async DOM updates and waiting for content
108126

109-
<docs-code header="src/app/animations.ts (excerpt)" path="adev/src/content/examples/animations/src/app/animations.ts" visibleRegion="style-view"/>
127+
* [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#async_dom_updates_and_waiting_for_content)
110128

111-
### Query the view containers
129+
> 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.
112130
113-
Use the `query()` method to find and animate elements within the current host component.
114-
The `query(":enter")` statement returns the view that is being inserted, and `query(":leave")` returns the view that is being removed.
131+
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.
115132

116-
Assume that you are routing from the *Home => About*.
133+
### Handle multiple view transition styles with view transition types
117134

118-
<docs-code header="src/app/animations.ts (excerpt)" path="adev/src/content/examples/animations/src/app/animations.ts" visibleRegion="query"/>
135+
* [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#view-transition-types)
136+
* [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-vxzcam)
119137

120-
The animation code does the following after styling the views:
138+
### Handle multiple view transition styles with a class name on the view transition root (deprecated)
121139

122-
1. `query(':enter', style({ left: '-100%' }))` matches the view that is added and hides the newly added view by positioning it to the far left.
123-
1. Calls `animateChild()` on the view that is leaving, to run its child animations.
124-
1. Uses [`group()`](api/animations/group) function to make the inner animations run in parallel.
125-
1. Within the [`group()`](api/animations/group) function:
126-
1. Queries the view that is removed and animates it to slide far to the right.
127-
1. Slides in the new view by animating the view with an easing function and duration.
140+
* [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#changing-on-navigation-type)
141+
* [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-nmnzzg?file=src%2Fmain.ts)
128142

129-
This animation results in the `about` view sliding in from the left.
143+
### Transitioning without freezing other animations
130144

131-
1. Calls the `animateChild()` method on the new view to run its child animations after the main animation completes.
145+
* [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#transitioning-without-freezing)
146+
* [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-76kgww)
132147

133-
You now have a basic routable animation that animates routing from one view to another.
148+
### Animating with Javascript
134149

135-
## More on Angular animations
150+
* [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#animating-with-javascript)
151+
* [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-cklnkm)
136152

137-
You might also be interested in the following:
153+
## Native View Transitions Alternative
138154

139-
<docs-pill-row>
140-
<docs-pill href="guide/animations" title="Introduction to Angular animations"/>
141-
<docs-pill href="guide/animations/transition-and-triggers" title="Transition and triggers"/>
142-
<docs-pill href="guide/animations/complex-sequences" title="Complex animation sequences"/>
143-
<docs-pill href="guide/animations/reusable-animations" title="Reusable animations"/>
144-
</docs-pill-row>
155+
Animating the transition between routes can also be done with the `@angular/animations` package.
156+
The animation [triggers and transitions](/guide/animations/transition-and-triggers)
157+
can be derived from the router state, such as the current URL or `ActivatedRoute`.

packages/router/src/provide_router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ export function withComponentInputBinding(): ComponentInputBindingFeature {
766766
* @returns A set of providers for use with `provideRouter`.
767767
* @see https://developer.chrome.com/docs/web-platform/view-transitions/
768768
* @see https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
769-
* @experimental
769+
* @developerPreview
770770
*/
771771
export function withViewTransitions(
772772
options?: ViewTransitionsFeatureOptions,

0 commit comments

Comments
 (0)