v1.1.0: pooled runtime, 959 tests, production hardening (0 squash)
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import type { GeometrySnapshot } from './snapshots.js'
|
||||
import type { StateMaterializer } from './states.js'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Transition Sampling Configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Mode that determines how a transition is sampled. */
|
||||
export type TransitionMode = 'keyframes' | 'range' | 'transition'
|
||||
|
||||
/**
|
||||
* Configuration for sampling a transition or animation.
|
||||
*
|
||||
* The materializer uses this to decide which time slices to capture.
|
||||
*/
|
||||
export interface TransitionSampleConfig {
|
||||
/** Sampling strategy. */
|
||||
mode: TransitionMode
|
||||
/** Target element selector for the transition. */
|
||||
selector: string
|
||||
/** Explicit keyframe timestamps in ms (keyframes mode). */
|
||||
keyframes?: number[]
|
||||
/** Start of the sampled range in ms (range mode). */
|
||||
from?: number
|
||||
/** End of the sampled range in ms (range mode). */
|
||||
to?: number
|
||||
/** Total duration of the transition in ms (transition mode). */
|
||||
duration?: number
|
||||
/** Number of evenly-spaced samples (transition mode). */
|
||||
sampleCount?: number
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Transition Sampler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Contract for sampling snapshots across a temporal range.
|
||||
*
|
||||
* The sampler receives a state materializer and a capture callback.
|
||||
* It is responsible for scrubbing or waiting to each sample time and
|
||||
* invoking the callback to produce a geometry snapshot.
|
||||
*/
|
||||
export interface TransitionSampler {
|
||||
/**
|
||||
* Sample the transition according to `config`.
|
||||
*
|
||||
* @param config - Sampling configuration.
|
||||
* @param materializer - State materializer used to set up the initial state.
|
||||
* @param capture - Callback that captures a single geometry snapshot.
|
||||
* @returns Array of snapshots in chronological order.
|
||||
*/
|
||||
sample(
|
||||
config: TransitionSampleConfig,
|
||||
materializer: StateMaterializer,
|
||||
capture: () => Promise<GeometrySnapshot>
|
||||
): Promise<GeometrySnapshot[]>
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sampling Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resolve the list of sample times (in ms) from a configuration.
|
||||
*
|
||||
* - keyframes: returns the provided keyframe array.
|
||||
* - range: returns [from, to].
|
||||
* - transition: returns evenly-spaced samples across duration.
|
||||
*/
|
||||
export function resolveSampleTimes(config: TransitionSampleConfig): number[] {
|
||||
switch (config.mode) {
|
||||
case 'keyframes': {
|
||||
return config.keyframes?.length ? config.keyframes : [0]
|
||||
}
|
||||
case 'range': {
|
||||
const from = config.from ?? 0
|
||||
const to = config.to ?? from
|
||||
return from === to ? [from] : [from, to]
|
||||
}
|
||||
case 'transition': {
|
||||
const duration = config.duration ?? 300
|
||||
const count = Math.max(2, config.sampleCount ?? 3)
|
||||
const step = duration / (count - 1)
|
||||
return Array.from({ length: count }, (_, i) => Math.round(i * step))
|
||||
}
|
||||
default: {
|
||||
// Exhaustiveness fallback.
|
||||
return [0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Default Transition Sampler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a transition sampler that captures a snapshot at each sample time.
|
||||
*
|
||||
* This is a simplified implementation suitable for browser-backed
|
||||
* measurement. A production sampler may use CDP animation scrubbing
|
||||
* or requestAnimationFrame timing for higher precision.
|
||||
*/
|
||||
export function createTransitionSampler(): TransitionSampler {
|
||||
return {
|
||||
async sample(config, _materializer, capture) {
|
||||
const times = resolveSampleTimes(config)
|
||||
const snapshots: GeometrySnapshot[] = []
|
||||
|
||||
for (const time of times) {
|
||||
// In a full implementation this would seek the animation to `time`
|
||||
// before capturing. Here we capture sequentially and annotate the
|
||||
// snapshot metadata with the intended sample time.
|
||||
const snapshot = await capture()
|
||||
if (typeof snapshot.metadata.durationMs === 'number') {
|
||||
snapshot.metadata.durationMs = time
|
||||
} else {
|
||||
snapshot.metadata.durationMs = time
|
||||
}
|
||||
snapshots.push(snapshot)
|
||||
}
|
||||
|
||||
return snapshots
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user