-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New events for Astro's view transition API (#9090)
* draft new view transition events * initial state for PR * remove intraPageTransitions flag based on review comments * add createAnimationScope after review comments * remove style elements from styles after review comments * remove quotes from animation css to enable set:text * added changeset * move scrollRestoration call from popstate handler to scroll update * Update .changeset/few-keys-heal.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Less confusing after following review comments * Less confusing after following review comments --------- Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
- Loading branch information
1 parent
ac908b7
commit c87223c
Showing
10 changed files
with
528 additions
and
171 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
--- | ||
'astro': minor | ||
--- | ||
Take full control over the behavior of view transitions! | ||
|
||
Three new events now complement the existing `astro:after-swap` and `astro:page-load` events: | ||
|
||
``` javascript | ||
astro:before-preparation // Control how the DOM and other resources of the target page are loaded | ||
astro:after-preparation // Last changes before taking off? Remove that loading indicator? Here you go! | ||
astro:before-swap // Control how the DOM is updated to match the new page | ||
``` | ||
|
||
The `astro:before-*` events allow you to change properties and strategies of the view transition implementation. | ||
The `astro:after-*` events are notifications that a phase is complete. | ||
Head over to docs to see [the full view transitions lifecycle](https://docs.astro.build/en/guides/view-transitions/#lifecycle-events) including these new events! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import { updateScrollPosition } from './router.js'; | ||
import type { Direction, NavigationTypeString } from './types.js'; | ||
|
||
export const TRANSITION_BEFORE_PREPARATION = 'astro:before-preparation'; | ||
export const TRANSITION_AFTER_PREPARATION = 'astro:after-preparation'; | ||
export const TRANSITION_BEFORE_SWAP = 'astro:before-swap'; | ||
export const TRANSITION_AFTER_SWAP = 'astro:after-swap'; | ||
export const TRANSITION_PAGE_LOAD = 'astro:page-load'; | ||
|
||
type Events = | ||
| typeof TRANSITION_AFTER_PREPARATION | ||
| typeof TRANSITION_AFTER_SWAP | ||
| typeof TRANSITION_PAGE_LOAD; | ||
export const triggerEvent = (name: Events) => document.dispatchEvent(new Event(name)); | ||
export const onPageLoad = () => triggerEvent(TRANSITION_PAGE_LOAD); | ||
|
||
/* | ||
* Common stuff | ||
*/ | ||
class BeforeEvent extends Event { | ||
readonly from: URL; | ||
to: URL; | ||
direction: Direction | string; | ||
readonly navigationType: NavigationTypeString; | ||
readonly sourceElement: Element | undefined; | ||
readonly info: any; | ||
newDocument: Document; | ||
|
||
constructor( | ||
type: string, | ||
eventInitDict: EventInit | undefined, | ||
from: URL, | ||
to: URL, | ||
direction: Direction | string, | ||
navigationType: NavigationTypeString, | ||
sourceElement: Element | undefined, | ||
info: any, | ||
newDocument: Document | ||
) { | ||
super(type, eventInitDict); | ||
this.from = from; | ||
this.to = to; | ||
this.direction = direction; | ||
this.navigationType = navigationType; | ||
this.sourceElement = sourceElement; | ||
this.info = info; | ||
this.newDocument = newDocument; | ||
|
||
Object.defineProperties(this, { | ||
from: { enumerable: true }, | ||
to: { enumerable: true, writable: true }, | ||
direction: { enumerable: true, writable: true }, | ||
navigationType: { enumerable: true }, | ||
sourceElement: { enumerable: true }, | ||
info: { enumerable: true }, | ||
newDocument: { enumerable: true, writable: true }, | ||
}); | ||
} | ||
} | ||
|
||
/* | ||
* TransitionBeforePreparationEvent | ||
*/ | ||
export const isTransitionBeforePreparationEvent = ( | ||
value: any | ||
): value is TransitionBeforePreparationEvent => value.type === TRANSITION_BEFORE_PREPARATION; | ||
export class TransitionBeforePreparationEvent extends BeforeEvent { | ||
formData: FormData | undefined; | ||
loader: () => Promise<void>; | ||
constructor( | ||
from: URL, | ||
to: URL, | ||
direction: Direction | string, | ||
navigationType: NavigationTypeString, | ||
sourceElement: Element | undefined, | ||
info: any, | ||
newDocument: Document, | ||
formData: FormData | undefined, | ||
loader: (event: TransitionBeforePreparationEvent) => Promise<void> | ||
) { | ||
super( | ||
TRANSITION_BEFORE_PREPARATION, | ||
{ cancelable: true }, | ||
from, | ||
to, | ||
direction, | ||
navigationType, | ||
sourceElement, | ||
info, | ||
newDocument | ||
); | ||
this.formData = formData; | ||
this.loader = loader.bind(this, this); | ||
Object.defineProperties(this, { | ||
formData: { enumerable: true }, | ||
loader: { enumerable: true, writable: true }, | ||
}); | ||
} | ||
} | ||
|
||
/* | ||
* TransitionBeforeSwapEvent | ||
*/ | ||
|
||
export const isTransitionBeforeSwapEvent = (value: any): value is TransitionBeforeSwapEvent => | ||
value.type === TRANSITION_BEFORE_SWAP; | ||
export class TransitionBeforeSwapEvent extends BeforeEvent { | ||
readonly direction: Direction | string; | ||
readonly viewTransition: ViewTransition; | ||
swap: () => void; | ||
|
||
constructor( | ||
afterPreparation: BeforeEvent, | ||
viewTransition: ViewTransition, | ||
swap: (event: TransitionBeforeSwapEvent) => void | ||
) { | ||
super( | ||
TRANSITION_BEFORE_SWAP, | ||
undefined, | ||
afterPreparation.from, | ||
afterPreparation.to, | ||
afterPreparation.direction, | ||
afterPreparation.navigationType, | ||
afterPreparation.sourceElement, | ||
afterPreparation.info, | ||
afterPreparation.newDocument | ||
); | ||
this.direction = afterPreparation.direction; | ||
this.viewTransition = viewTransition; | ||
this.swap = swap.bind(this, this); | ||
|
||
Object.defineProperties(this, { | ||
direction: { enumerable: true }, | ||
viewTransition: { enumerable: true }, | ||
swap: { enumerable: true, writable: true }, | ||
}); | ||
} | ||
} | ||
|
||
export async function doPreparation( | ||
from: URL, | ||
to: URL, | ||
direction: Direction | string, | ||
navigationType: NavigationTypeString, | ||
sourceElement: Element | undefined, | ||
info: any, | ||
formData: FormData | undefined, | ||
defaultLoader: (event: TransitionBeforePreparationEvent) => Promise<void> | ||
) { | ||
const event = new TransitionBeforePreparationEvent( | ||
from, | ||
to, | ||
direction, | ||
navigationType, | ||
sourceElement, | ||
info, | ||
window.document, | ||
formData, | ||
defaultLoader | ||
); | ||
if (document.dispatchEvent(event)) { | ||
await event.loader(); | ||
if (!event.defaultPrevented) { | ||
triggerEvent(TRANSITION_AFTER_PREPARATION); | ||
if (event.navigationType !== 'traverse') { | ||
// save the current scroll position before we change the DOM and transition to the new page | ||
updateScrollPosition({ scrollX, scrollY }); | ||
} | ||
} | ||
} | ||
return event; | ||
} | ||
|
||
export async function doSwap( | ||
afterPreparation: BeforeEvent, | ||
viewTransition: ViewTransition, | ||
defaultSwap: (event: TransitionBeforeSwapEvent) => void | ||
) { | ||
const event = new TransitionBeforeSwapEvent(afterPreparation, viewTransition, defaultSwap); | ||
document.dispatchEvent(event); | ||
event.swap(); | ||
return event; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.