@@ -5,24 +5,29 @@ contributors:
5
5
---
6
6
7
7
# View Transition API
8
+
8
9
By default Qwik will start a view transition when SPA navigation. We can run animation either with CSS or WAAPI.
9
10
10
11
## CSS
12
+
11
13
``` tsx
12
14
export default component$ (({ list }) => {
13
15
return (
14
16
<ul >
15
17
{ list .map ((item ) => (
16
18
// Create a name per item
17
- <li key = { item .id } class = " item" style = { {viewTransitionName: ` _${item .id }_ ` }} >...</li >
19
+ <li key = { item .id } class = " item" style = { { ' --view-transition-name' : ` _${item .id }_ ` }} >
20
+ ...
21
+ </li >
18
22
))}
19
23
</ul >
20
- )
21
- })
24
+ );
25
+ });
22
26
```
23
27
24
28
``` css
25
29
.item {
30
+ view-transition-name : var (--view-transition-name );
26
31
/* Alias to target all .item with a view-transition-name */
27
32
view-transition-class : animated-item;
28
33
}
@@ -34,35 +39,50 @@ export default component$(({ list }) => {
34
39
::view-transition-old(.animated-item ):only-child {
35
40
animation : fade-out 200ms ;
36
41
}
42
+
43
+ /* If view transition type is supported */
44
+ @supports selector (html :active-view-transition-type(type)) {
45
+ /* Remove view transition name when view transition is not "qwik-navigation" */
46
+ html :not (:active-view-transition-type(qwik-navigation )) .items {
47
+ view-transition-name : none ;
48
+ }
49
+ }
37
50
```
38
51
39
- Sometime we need to have some specific logic before the animation start. In this case you can listen to the ` qviewTransition ` event.
52
+ Sometime we need to have some specific logic before the animation start. In this case you can listen to the ` qviewtransition ` event.
40
53
41
54
For example if you want to only animate visible element:
55
+
42
56
``` tsx
43
57
export default component$ (() => {
44
58
// In this case we need the callback to be sync, else the transition might have already happened
45
- useOnDocument (' qviewTransition' , sync$ ((event : CustomEvent <ViewTransition >) => {
46
- const transition = event .detail ;
47
- const items = document .querySelectorAll (' .item' );
48
- for (const item of items ) {
49
- if (! item .checkVisibility ()) continue ;
50
- item .dataset .hasViewTransition = true ;
51
- }
52
- }))
59
+ useOnDocument (
60
+ ' qviewtransition' ,
61
+ sync$ ((event : CustomEvent <ViewTransition >) => {
62
+ const transition = event .detail ;
63
+ const items = document .querySelectorAll (' .item' );
64
+ for (const item of items ) {
65
+ if (! item .checkVisibility ()) continue ;
66
+ item .dataset .hasViewTransition = true ;
67
+ }
68
+ })
69
+ );
53
70
return (
54
71
<ul >
55
72
{ list .map ((item ) => (
56
73
// Create a name per item
57
- <li key = { item .id } class = " item" style = { {viewTransitionName: ` _${item .id }_ ` }} >...</li >
74
+ <li key = { item .id } class = " item" style = { { ' --view-transition-name' : ` _${item .id }_ ` }} >
75
+ ...
76
+ </li >
58
77
))}
59
78
</ul >
60
- )
61
- })
79
+ );
80
+ });
62
81
```
63
82
64
83
``` css
65
- .item [data-has-view-transition = " true" ] {
84
+ .item [data-has-view-transition = ' true' ] {
85
+ view-transition-name : var (--view-transition-name );
66
86
view-transition-class : animated-item;
67
87
}
68
88
::view-transition-new(.animated-item ):only-child {
@@ -71,14 +91,24 @@ export default component$(() => {
71
91
::view-transition-old(.animated-item ):only-child {
72
92
animation : fade-out 200ms ;
73
93
}
94
+
95
+ /* If view transition type is supported */
96
+ @supports selector (html :active-view-transition-type(type)) {
97
+ /* Remove view transition name when view transition is not "qwik-navigation" */
98
+ html :not (:active-view-transition-type(qwik-navigation )) .items [data-has-view-transition = ' true' ] {
99
+ view-transition-name : none ;
100
+ }
101
+ }
74
102
```
75
103
76
104
> ** Note** : ` ViewTransition ` interface is available with Typescript >5.6.
77
105
78
106
## WAAPI
107
+
79
108
With Web Animation API you can get more precise, but for that we need to wait for the ::view-transition pseudo-element to exist in the DOM. To achieve that you can wait the ` transition.ready ` promise.
80
109
81
110
In this example we add some delay for each item :
111
+
82
112
``` tsx
83
113
export default component$ (() => {
84
114
// Remove default style on the pseudo-element.
@@ -90,41 +120,72 @@ export default component$(() => {
90
120
animation: none;
91
121
}
92
122
` );
93
- useOnDocument (' qviewTransition' , $ (async (event : CustomEvent <ViewTransition >) => {
94
- // Get visible item's viewTransitionName (should happen before transition is ready)
95
- const items = document .querySelectorAll <HTMLElement >(' .item' );
96
- const names = Array .from (items )
97
- .filter ((item ) => item .checkVisibility ())
98
- .map ((item ) => item .style .viewTransitionName );
99
-
100
- // Wait for ::view-transition pseudo-element to exist
101
- const transition = event .detail ;
102
- await transition .ready ;
103
-
104
- // Animate each leaving item
105
- for (let i = 0 ; i < names .length ; i ++ ) {
106
- // Note: we animate the <html> element
107
- document .documentElement .animate ({
108
- opacity: 0 ,
109
- transform: ' scale(0.9)'
110
- }, {
111
- // Target the pseudo-element inside the <html> element
112
- pseudoElement: ` ::view-transition-old(${names [i ]}) ` ,
113
- duration: 200 ,
114
- fill: " forwards" ,
115
- delay: i * 50 , // Add delay for each pseudo-element
116
- })
117
- }
118
- }))
123
+ useOnDocument (
124
+ ' qviewtransition' ,
125
+ $ (async (event : CustomEvent <ViewTransition >) => {
126
+ // Get visible item's viewTransitionName (should happen before transition is ready)
127
+ const items = document .querySelectorAll <HTMLElement >(' .item' );
128
+ const names = Array .from (items )
129
+ .filter ((item ) => item .checkVisibility ())
130
+ .map ((item ) => item .style .viewTransitionName );
131
+
132
+ // Wait for ::view-transition pseudo-element to exist
133
+ const transition = event .detail ;
134
+ await transition .ready ;
135
+
136
+ // Animate each leaving item
137
+ for (let i = 0 ; i < names .length ; i ++ ) {
138
+ // Note: we animate the <html> element
139
+ document .documentElement .animate (
140
+ {
141
+ opacity: 0 ,
142
+ transform: ' scale(0.9)' ,
143
+ },
144
+ {
145
+ // Target the pseudo-element inside the <html> element
146
+ pseudoElement: ` ::view-transition-old(${names [i ]}) ` ,
147
+ duration: 200 ,
148
+ fill: ' forwards' ,
149
+ delay: i * 50 , // Add delay for each pseudo-element
150
+ }
151
+ );
152
+ }
153
+ })
154
+ );
119
155
return (
120
156
<ul >
121
157
{ list .map ((item ) => (
122
158
// Create a name per item
123
- <li key = { item .id } class = " item" style = { {viewTransitionName: ` _${item .id }_ ` }} >...</li >
159
+ <li key = { item .id } class = " item" style = { { viewTransitionName: ` _${item .id }_ ` }} >
160
+ ...
161
+ </li >
124
162
))}
125
163
</ul >
126
- )
127
- })
164
+ );
165
+ });
128
166
```
129
167
168
+ When listening on the ` qviewTransition ` we know that
169
+
130
170
> ** Note** : For it to work correctly, we need to ** remove the default view transition** animation else it happens on top of the ` .animate() ` . I'm using ` view-transition-class ` which is only working with Chrome right now.
171
+
172
+ ## Root transition
173
+
174
+ By default Qwik disables root view transition inside the ` @layer qwik ` . If you want to enable it you need to reset it :
175
+
176
+ ``` css
177
+ :root {
178
+ view-transition-name : root;
179
+ }
180
+ ```
181
+
182
+ or within a ` @layer ` :
183
+
184
+ ``` css
185
+ @layer qwik, reset;
186
+ @layer reset {
187
+ :root {
188
+ view-transition-name : root;
189
+ }
190
+ }
191
+ ```
0 commit comments