Skip to content

packages/core/src/lib/transitions/define-transition.ts

Read as Markdown

This is the source snapshot used to build these API details. View this revision on GitHub.

Back to reference

1 import { opacity } from '../../filters/native.js';
2 import { withSignalWriteForbidden } from '../signals/graph.js';
3 import { captureTransition, finite, type TransitionRegion } from './capture.js';
4 import { defineTransitionEffect, type PibblTransitionEffect } from './fade.js';
5 
6 /**
7  * Progress, geometry, and drawing context supplied to a custom transition renderer.
8  *
9  * @see {@link PibblTransitionDefinition}
10  */
11 export interface PibblTransitionRenderFrame {
12   /** Borrowed for this synchronous callback only. Coordinates are receiver-local. */
13   readonly context: OffscreenCanvasRenderingContext2D;
14   /** Captured source pixels available during this synchronous rendering callback. See {@link PibblTransitionRenderFrame}. */
15   readonly source: OffscreenCanvas;
16   /** Bounds of the source pixels supplied to the transition. See {@link TransitionRegion}. */
17   readonly sourceBounds: Readonly<TransitionRegion>;
18   /**
19    * Normalized progress through the current animation or transition. See
20    * {@link PibblTransitionRenderFrame}.
21    */
22   readonly progress: number;
23   /** Selects `"enter"`, `"exit"` for phase. See {@link PibblTransitionRenderFrame}. */
24   readonly phase: 'enter' | 'exit';
25   /** Forward milliseconds since this run began, unaffected by reversals. */
26   readonly elapsed: number;
27   /**
28    * Material progress at an earlier time in this run (useful for particle births).
29    * @param elapsed - Elapsed transition time in milliseconds.
30    * @returns Transition progress at the requested time.
31    */
32   readonly progressAt: (elapsed: number) => number;
33   /**
34    * Draws the captured source into the transition's current rendering context. See
35    * {@link PibblTransitionRenderFrame}.
36    */
37   readonly drawSource: () => void;
38 }
39 /**
40  * Synchronous transition rendering callback and optional post-transition linger duration.
41  *
42  * @see {@link PibblTransitionRenderFrame}
43  * @see {@link defineTransition}
44  */
45 export interface PibblTransitionDefinition {
46   /**
47    * Renders the current data or resources using the supplied context. See
48    * {@link PibblTransitionRenderFrame}.
49    * @param frame - Captured source, presentation context, progress, and timing services. See
50    * {@link PibblTransitionRenderFrame} .
51    */
52   readonly render: (frame: Readonly<PibblTransitionRenderFrame>) => void;
53   /** Additional milliseconds after material reaches its target. Default zero. */
54   readonly linger?: number;
55 }
56 /**
57  * Inert custom definition. The callback may call synchronous JS or WASM kernels.
58  *
59  * @param definition - Renderer and metadata defining the effect. See
60  * {@link PibblTransitionDefinition} .
61  * @returns A transition effect usable by the visibility transition hook. See
62  * {@link PibblTransitionEffect} .
63  *
64  * @see {@link PibblTransitionDefinition}
65  * @see {@link PibblTransitionEffect}
66  */
67 export function defineTransition(definition: PibblTransitionDefinition): PibblTransitionEffect {
68   const render = definition.render, linger = finite(definition.linger ?? 0, 'linger', 0);
69   if (typeof render !== 'function') throw new TypeError('defineTransition requires a synchronous render callback.');
70   return defineTransitionEffect((progress, phase, frame) => {
71     if (!frame.active || !linger) {
72       if (progress === 1) return [];
73       if (progress === 0) return [opacity({ amount: 0 })];
74     }
75     return [captureTransition(({ context, source, windows, drawSource }) => {
76       const result: unknown = withSignalWriteForbidden('Transition render callbacks cannot write to signals.', () => render(Object.freeze({ context, source,
77         sourceBounds: Object.freeze({ ...windows.source.logicalBounds }), progress, phase,
78         elapsed: frame.elapsed, progressAt: frame.progressAt, drawSource,
79       })));
80       if (result && typeof (result as { then?: unknown }).then === 'function') throw new TypeError('Transition render callbacks must be synchronous.');
81     })];
82   }, linger);
83 }
84 

Documentation built with @pibbl/core 0.0.2, revision 2dccb19. ALPHA — NOT FOR PRODUCTION USE.