@@ -18,34 +18,51 @@ const focusController = createFocusController();
1818
1919// TODO(FW-2832): types 
2020
21+ /** 
22+  * Executes the main page transition. 
23+  * It also manages the lifecycle of header visibility (if any) 
24+  * to prevent visual flickering in iOS. The flickering only 
25+  * occurs for a condensed header that is placed above the content. 
26+  * 
27+  * @param  opts Options for the transition. 
28+  * @returns  A promise that resolves when the transition is complete. 
29+  */ 
2130export  const  transition  =  ( opts : TransitionOptions ) : Promise < TransitionResult >  =>  { 
2231  return  new  Promise ( ( resolve ,  reject )  =>  { 
2332    writeTask ( ( )  =>  { 
24-       beforeTransition ( opts ) ; 
25-       runTransition ( opts ) . then ( 
26-         ( result )  =>  { 
27-           if  ( result . animation )  { 
28-             result . animation . destroy ( ) ; 
33+       const  transitioningInactiveHeader  =  getIosIonHeader ( opts ) ; 
34+       beforeTransition ( opts ,  transitioningInactiveHeader ) ; 
35+       runTransition ( opts ) 
36+         . then ( 
37+           ( result )  =>  { 
38+             if  ( result . animation )  { 
39+               result . animation . destroy ( ) ; 
40+             } 
41+             afterTransition ( opts ) ; 
42+             resolve ( result ) ; 
43+           } , 
44+           ( error )  =>  { 
45+             afterTransition ( opts ) ; 
46+             reject ( error ) ; 
2947          } 
30-           afterTransition ( opts ) ; 
31-           resolve ( result ) ; 
32-         } , 
33-         ( error )  =>  { 
34-           afterTransition ( opts ) ; 
35-           reject ( error ) ; 
36-         } 
37-       ) ; 
48+         ) 
49+         . finally ( ( )  =>  { 
50+           // Ensure that the header is restored to its original state. 
51+           setHeaderTransitionClass ( transitioningInactiveHeader ,  false ) ; 
52+         } ) ; 
3853    } ) ; 
3954  } ) ; 
4055} ; 
4156
42- const  beforeTransition  =  ( opts : TransitionOptions )  =>  { 
57+ const  beforeTransition  =  ( opts : TransitionOptions ,   transitioningInactiveHeader :  HTMLElement   |   null )  =>  { 
4358  const  enteringEl  =  opts . enteringEl ; 
4459  const  leavingEl  =  opts . leavingEl ; 
4560
4661  focusController . saveViewFocus ( leavingEl ) ; 
4762
4863  setZIndex ( enteringEl ,  leavingEl ,  opts . direction ) ; 
64+   // Prevent flickering of the header by adding a class. 
65+   setHeaderTransitionClass ( transitioningInactiveHeader ,  true ) ; 
4966
5067  if  ( opts . showGoBack )  { 
5168    enteringEl . classList . add ( 'can-go-back' ) ; 
@@ -278,6 +295,36 @@ const setZIndex = (
278295  } 
279296} ; 
280297
298+ /** 
299+  * Add a class to ensure that the inactive header (if any) 
300+  * does not flicker during the transition. By adding the 
301+  * transitioning class, we ensure that the header has 
302+  * the necessary styles to prevent the following flickers: 
303+  * 1. When entering a page with a condensed header, the 
304+  * inactive header should never be visible. However, 
305+  * it briefly renders the background color while 
306+  * the transition is occurring. 
307+  * 2. When leaving a page with a condensed header, the 
308+  * inactive header has an opacity of 0 and the pages 
309+  * have a z-index which causes the entering page to 
310+  * briefly show it's content underneath the leaving page. 
311+  * 
312+  * @param  header The header element to modify. 
313+  * @param  isTransitioning Whether the transition is occurring. 
314+  */ 
315+ const  setHeaderTransitionClass  =  ( header : HTMLElement  |  null ,  isTransitioning : boolean )  =>  { 
316+   if  ( ! header )  { 
317+     return ; 
318+   } 
319+ 
320+   const  transitionClass  =  'header-transitioning' ; 
321+   if  ( isTransitioning )  { 
322+     header . classList . add ( transitionClass ) ; 
323+   }  else  { 
324+     header . classList . remove ( transitionClass ) ; 
325+   } 
326+ } ; 
327+ 
281328export  const  getIonPageElement  =  ( element : HTMLElement )  =>  { 
282329  if  ( element . classList . contains ( 'ion-page' ) )  { 
283330    return  element ; 
@@ -291,6 +338,32 @@ export const getIonPageElement = (element: HTMLElement) => {
291338  return  element ; 
292339} ; 
293340
341+ /** 
342+  * Retrieves the ion-header element from a page based on the 
343+  * direction of the transition. 
344+  * 
345+  * @param  opts Options for the transition. 
346+  * @returns  The ion-header element or null if not found or not in 'ios' mode. 
347+  */ 
348+ const  getIosIonHeader  =  ( opts : TransitionOptions ) : HTMLElement  |  null  =>  { 
349+   const  enteringEl  =  opts . enteringEl ; 
350+   const  leavingEl  =  opts . leavingEl ; 
351+   const  direction  =  opts . direction ; 
352+   const  mode  =  opts . mode ; 
353+ 
354+   if  ( mode  !==  'ios' )  { 
355+     return  null ; 
356+   } 
357+ 
358+   const  element  =  direction  ===  'back'  ? leavingEl  : enteringEl ; 
359+ 
360+   if  ( ! element )  { 
361+     return  null ; 
362+   } 
363+ 
364+   return  element . querySelector ( 'ion-header' ) ; 
365+ } ; 
366+ 
294367export  interface  TransitionOptions  extends  NavOptions  { 
295368  progressCallback ?: ( ani : Animation  |  undefined )  =>  void ; 
296369  baseEl : any ; 
0 commit comments