Skip to content

API Overview

SymbolPurposeExecution modeCommon gotcha
animate()Animate one elementSyncOmitted timing fields use native WAAPI behavior
animateEach()Animate a unique element groupSyncNon-zero stagger needs numeric delay
captureLayout()Capture positions and create a one-shot FLIP transitionSyncCapture before changing layout
NecromancerErrorBase package errorSyncUse instanceof NecromancerError to narrow unknown errors

Package Entry Point

ImportPurpose
@vielzeug/necromancerAnimation functions, types, and errors
@vielzeug/necromancer/testingjsdom test fakes for Element.animate() and getBoundingClientRect()

Animation Functions

animate()

ts
function animate(element: Element, keyframes: Keyframes, options?: AnimateOptions): AnimationHandle;

Starts a lifecycle-owned native Web Animation. Native timing options, including an omitted or zero duration, are preserved.

ParameterTypeDescription
elementElementElement animated through its native animate() method.
keyframesKeyframesNative keyframe array or property-indexed keyframes.
optionsAnimateOptionsNative timing plus motion and cancellation ownership.

Returns: An AnimationHandle with native playback access and explicit disposal.

Example:

ts
import { animate } from '@vielzeug/necromancer';

const handle = animate(element, [{ opacity: 0 }, { opacity: 1 }], { duration: 180 });
handle.animation.pause();
const result = await handle.result;
handle.dispose();

animateEach()

ts
function animateEach<ElementType extends Element>(
  elements: Iterable<ElementType>,
  keyframes: Keyframes | KeyframeFactory<ElementType>,
  options?: AnimateEachOptions,
): AnimationGroup;

Starts animations for unique elements in first-seen order. Necromancer resolves every keyframe factory before starting the first native animation.

ParameterTypeDescription
elementsIterable<ElementType>Elements animated once in first-seen order.
keyframesKeyframes | KeyframeFactory<ElementType>Shared keyframes or a subtype-preserving factory.
optionsAnimateEachOptionsAnimation options plus a stagger interval.

Returns: An AnimationGroup containing child handles and ordered results.

Example:

ts
import { animateEach } from '@vielzeug/necromancer';

const group = animateEach(document.querySelectorAll<HTMLElement>('.item'), [{ opacity: 0 }, { opacity: 1 }], {
  duration: 180,
  stagger: 30,
});
await group.results;

Layout Functions

captureLayout()

ts
function captureLayout<ElementType extends Element>(
  elements: Iterable<ElementType>,
  options?: LayoutCaptureOptions<ElementType>,
): LayoutTransition<ElementType>;

Captures unique elements' positions and sizes and returns a one-shot transition. Rotation and other transforms are not captured or compensated.

ParameterTypeDescription
elementsIterable<ElementType>Elements measured before a layout change.
optionsLayoutCaptureOptions<ElementType>Optional stable key mapping for replacement nodes.

Returns: A LayoutTransition<ElementType> that measures committed layout once and creates an AnimationGroup.

Example:

ts
import { captureLayout } from '@vielzeug/necromancer';

const transition = captureLayout(beforeItems, {
  getKey: (element) => element.getAttribute('data-id')!,
});

renderReorderedItems();

const group = transition.animate({
  duration: 220,
  easing: 'ease-out',
  elements: afterItems,
});

Calling animate() twice on the same transition throws NecromancerConfigError.

Types

MotionMode

ts
type MotionMode = 'full' | 'reduced' | 'system';

'system' is the default. Reduced motion preserves the supplied keyframes while normalizing delay, duration, and end delay to 0, and iterations to 1.

AnimationResult

ts
type AnimationResult =
  | { readonly status: 'finished' }
  | { readonly status: 'reduced' }
  | { readonly reason?: unknown; readonly status: 'cancelled' };

cancelled describes native cancellation and includes its native rejection reason. A reason passed to dispose() or an abort signal takes precedence. The independent disposed property becomes true only when the lifecycle owner is explicitly disposed.

AnimateOptions

ts
type AnimateOptions = KeyframeAnimationOptions & {
  readonly motion?: MotionMode;
  readonly signal?: AbortSignal;
};

Native timing fields pass through unchanged unless reduced motion normalizes timing. signal disposes the returned handle when aborted.

AnimateEachOptions

ts
type AnimateEachOptions = AnimateOptions & {
  readonly stagger?: number;
};

stagger is a finite, non-negative millisecond offset.

LayoutCaptureOptions

ts
interface LayoutCaptureOptions<ElementType extends Element = Element> {
  readonly getKey?: (element: ElementType) => string;
}

getKey maps a captured element and its committed replacement to the same stable, non-empty string. Duplicate or empty keys throw NecromancerConfigError.

LayoutAnimationOptions

ts
type LayoutAnimationOptions<ElementType extends Element = Element> = AnimateEachOptions & {
  readonly elements?: Iterable<ElementType>;
};

elements is the collection in its committed layout. Omit it to animate the same captured elements. With getKey, replacement elements animate from the positions of their captured predecessors. Unmatched, removed, and newly entered elements are ignored.

Keyframes and KeyframeFactory

ts
type Keyframes = readonly Keyframe[] | PropertyIndexedKeyframes;
type KeyframeFactory<ElementType extends Element = Element> = (
  element: ElementType,
  index: number,
  total: number,
) => Keyframes;

Accepts a readonly array so a reusable as const keyframe list can be passed without a cast.

AnimationHandle

ts
interface AnimationHandle {
  readonly animation: Animation;
  readonly result: Promise<AnimationResult>;
  readonly disposed: boolean;
  dispose(reason?: unknown): void;
  [Symbol.dispose](): void;
}
MemberDescription
animationOwned native animation used for playback control.
resultResolves with the first terminal outcome.
disposedReports explicit owner disposal.
dispose(reason?)Cancels the animation and releases its abort listener.
[Symbol.dispose]()Delegates to dispose().

AnimationGroup

ts
interface AnimationGroup {
  readonly handles: readonly AnimationHandle[];
  readonly results: Promise<readonly AnimationResult[]>;
  readonly disposed: boolean;
  dispose(reason?: unknown): void;
  [Symbol.dispose](): void;
}
MemberDescription
handlesChild handles in input order.
resultsResolves with every child outcome in handle order.
disposedReports explicit group disposal.
dispose(reason?)Disposes every child.
[Symbol.dispose]()Delegates to dispose().

LayoutTransition

ts
interface LayoutTransition<ElementType extends Element = Element> {
  animate(options?: LayoutAnimationOptions<ElementType>): AnimationGroup;
}
MethodDescription
animate(options?)Measures committed elements and creates the one allowed layout animation group.

Errors

ErrorTrigger
NecromancerErrorBase class for package errors
NecromancerConfigErrorInvalid stagger, incompatible delay, or reused layout transition
NecromancerUnsupportedErrorElement.animate() is unavailable

Testing (@vielzeug/necromancer/testing)

jsdom (and most non-browser DOM environments) do not implement Element.animate(). Import these from the /testing sub-path, not the root entry point.

AnimationCall

ts
type AnimationCall = {
  readonly animation: FakeAnimation;
  readonly keyframes: Keyframe[] | PropertyIndexedKeyframes;
  readonly options?: KeyframeAnimationOptions;
};

One recorded invocation of Element.prototype.animate from installFakeAnimations().

installFakeAnimations()

ts
function installFakeAnimations(): {
  readonly calls: readonly AnimationCall[];
  restore(): void;
  [Symbol.dispose](): void;
};

Replaces Element.prototype.animate with a deterministic lifecycle fake for the duration of a test. calls records every invocation in order. Call restore() or use an explicit resource-management using declaration to put the original implementation back. The fake does not implement native playback controls such as pause() or reverse().

ts
import { installFakeAnimations } from '@vielzeug/necromancer/testing';

using animations = installFakeAnimations();
const handle = animate(element, [{ opacity: 0 }, { opacity: 1 }]);

animations.calls[0]?.animation.finish();
await handle.result; // { status: 'finished' }

FakeAnimation

ts
class FakeAnimation {
  cancelCallCount: number;
  finishCallCount: number;
  finished: Promise<void>;
  cancel(): void;
  finish(): void;
}

A minimal Animation stand-in. cancel() rejects finished with an AbortError; finish() resolves it. cancelCallCount/finishCallCount track how many times each was called, in place of a test-runner-specific spy.

createRect()

ts
function createRect(x: number, y: number, width?: number, height?: number): DOMRect;

Builds a DOMRect for mocking Element.getBoundingClientRect() in captureLayout() tests. width/height default to 20.