Using the View Transition API on Angular Framework
Tags: angular, javascript, webdev
Published: 2024-01-21 18:54:42

Recently the framework Angular launched exiciting features in the V17 version, in this article we will learn how to start using the View Transition API. This feature does not have good documentation, the idea here is to **explain in practice**.

In the demo project, we simulate a list of products, and when the user selects one, the application opens a new route with a smooth transition.

> The View Transitions API is still experimental in both (browsers and Angular), you can check the actual support here https://caniuse.com/view-transitions.

### Without View Transition enabled
![Interface working without the transition](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/defxex071i81riops3xx.gif)

## Enabling View Transition with Angular
The first thing you need to do is to include the `withViewTransitions()` along with the `proviceRouter`.

```javascript
const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'product/:id', component: ProductComponent },
];

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

### With default View Transition effect

![Interface working with the API enabled](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmi9sbscrzmwqnxrbole.gif)


## Customizing the default animation

```css
@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes fade-out {
  to {
    opacity: 0;
  }
}

::view-transition-old(root) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out;
}

::view-transition-new(root) {
  animation: 2s cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in;
}
```

## Animating isolated elements
Is important that each element have a unique name, in a `@for` example, if you set the same name for each element in the loop, the view transition will not work.

``` html
<h1>Products</h1>
<div class="grid">
  @for(product of products; track product.id) {
  <a
    routerLink="product/coffee-65324"
    (mouseenter)="setCurrentId(product.id)"
    class="card"
  >
    <header>
      <img
        [style.view-transition-name]="returnViewTransition(product.id)"
        src="https://picsum.photos/id/63/367/267"
      />
    </header>

    <div class="content">
      <h2 class="card__title">{{ product.name }}</h2>
      <p class="card__price">$3</p>
    </div>
  </a>
  }
</div>
```

In the path "/", our list have a loop of products, in each item, we will add:

``` html
 <img
   [style.view-transition-name]="returnViewTransition(product.id)"
   src="https://picsum.photos/id/63/367/267"
 />
```

As mentioned, we need to respect that only one element will have the property `view-transition-name: banner`, this little method will ensure to return it at the right moment. My strategy here was:

- Create a small service inject root with a parameter signal to store the current id that the user is navigating with the cursor.
- `returnViewTransition` will return dinamic based on the comparison of the product id in the loop and the current id in the signal.

Inside the `/product/:id` route, we have this block:

``` html
<header class="hero">
  <img src="https://picsum.photos/id/63/1280/720" />
  <button class="close-btn" routerLink="/">Back</button>
</header>
```

Now we need to tell the browser the `view-transition-name` associated with the home image:

``` css
.hero img {
  width: 100%;
  view-transition-name: banner;
}
```


### Final result
![Interface with all customizations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhq0chby4rpafjkz50p5.gif)

---

### Demo project
https://stackblitz.com/edit/stackblitz-starters-ba6uus?file=src%2Fpages%2Fhome%2Fhome.component.html

---

#### References
1. https://developer.chrome.com/docs/web-platform/view-transitions/
2. https://angular.dev/guide/signals#writable-signals
3. https://blog.angular.io/check-out-angulars-support-for-the-view-transitions-api-3937376cfc19