group
Names a set of fields, so an actor reads card.typography.size and spawns with nested inputs.
group puts fields under one name. The fields inside stay what they were: signals, derived
values, channels or other groups. A group adds a level to the path and nothing else, so it
changes how a value is read and spawned and never how it animates.
import { function group<T extends Record<string, unknown>>(value: T): T & SignalGroupDefinitiongroup, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor, function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it.
Without an initial value the signal is required: `ctx.spawn` must supply it.signal } from "@motionactor/core";
export class class CardCard extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor {
Card.typography: {
size: ActorFieldSignal<number>;
color: ActorFieldSignal<string>;
shadow: {
blur: ActorFieldSignal<number>;
} & SignalGroupDefinition;
} & SignalGroupDefinition
typography = group<{
size: ActorFieldSignal<number>;
color: ActorFieldSignal<string>;
shadow: {
blur: ActorFieldSignal<number>;
} & SignalGroupDefinition;
}>(value: {
size: ActorFieldSignal<number>;
color: ActorFieldSignal<string>;
shadow: {
blur: ActorFieldSignal<number>;
} & SignalGroupDefinition;
}): {
size: ActorFieldSignal<number>;
color: ActorFieldSignal<string>;
shadow: {
blur: ActorFieldSignal<number>;
} & SignalGroupDefinition;
} & SignalGroupDefinition
group({
size: ActorFieldSignal<number>size: signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)A timeline value on an actor, and an input to every spawn of it.
Without an initial value the signal is required: `ctx.spawn` must supply it.signal(48),
color: ActorFieldSignal<string>color: signal<string>(initial: string): ActorFieldSignal<string> (+1 overload)A timeline value on an actor, and an input to every spawn of it.
Without an initial value the signal is required: `ctx.spawn` must supply it.signal("#ffffff"),
shadow: {
blur: ActorFieldSignal<number>;
} & SignalGroupDefinition
shadow: group<{
blur: ActorFieldSignal<number>;
}>(value: {
blur: ActorFieldSignal<number>;
}): {
blur: ActorFieldSignal<number>;
} & SignalGroupDefinition
group({ blur: ActorFieldSignal<number>blur: signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)A timeline value on an actor, and an input to every spawn of it.
Without an initial value the signal is required: `ctx.spawn` must supply it.signal(0) }),
});
}
import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene } from "@motionactor/core";
import { class CardCard } from "./card";
const const compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled = createScene<{
card: Card;
}, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene(function* (ctx: SceneContextctx) {
const const card: Cardcard = ctx: SceneContextctx.CompileContext.spawn<Card>(definition: ActorClass<Card>, overrides?: {
typography?: {
size?: SignalInputValue<number> | undefined;
color?: SignalInputValue<string> | undefined;
shadow?: {
blur?: SignalInputValue<number> | undefined;
} | undefined;
} | undefined;
measuredBox?: undefined;
layout?: {
x?: SignalInputValue<LayoutValue> | undefined;
y?: SignalInputValue<LayoutValue> | undefined;
width?: Readonly<{
kind: "layout.percent";
value: number;
}> | SignalInputValue<...> | undefined;
height?: Readonly<...> | ... 1 more ... | undefined;
} | undefined;
... 20 more ...;
parent?: undefined;
} | undefined, options?: {
...;
} | undefined): Card (+1 overload)
spawn(class CardCard, {
typography?: {
size?: SignalInputValue<number> | undefined;
color?: SignalInputValue<string> | undefined;
shadow?: {
blur?: SignalInputValue<number> | undefined;
} | undefined;
} | undefined
typography: { size?: SignalInputValue<number> | undefinedsize: 64, shadow?: {
blur?: SignalInputValue<number> | undefined;
} | undefined
shadow: { blur?: SignalInputValue<number> | undefinedblur: 8 } },
});
yield* const card: Cardcard.Card.typography: {
size: ActorFieldSignal<number>;
color: ActorFieldSignal<string>;
shadow: {
blur: ActorFieldSignal<number>;
} & SignalGroupDefinition;
} & SignalGroupDefinition
typography.shadow: {
blur: ActorFieldSignal<number>;
} & SignalGroupDefinition
shadow.blur: ActorFieldSignal<number>blur.LiveSignal<number>.tween(target: number, options: number | SignalTweenOptions<number>): Generator<Frame, void, unknown>Tween to target over authored seconds — must yield*tween(0, 0.5);
return { card: Cardcard };
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });
const compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
card: Card;
}>
at(0).FrameState<{ ...; }>.scene: {
card: ResolvedActorOfInstance<Card> | undefined;
}
scene.card: ResolvedActorOfInstance<Card> | undefinedcard?.typography: {
size: number;
color: string;
shadow: {
blur: number;
};
} | undefined
typography;
// { size: 64, color: "#ffffff", shadow: { blur: 8 } }
Spawn inputs nest the same way. Fields left out keep their defaults, so color stays white.
In reflection and editors each field has its dotted path, such as typography.shadow.blur.
A subclass inherits its parent’s groups. Redeclaring a group in a subclass replaces the whole group, so restate every field it keeps.
The groups every LayoutActor has
| Group | Fields |
|---|---|
layout |
x, y, width, height |
transform |
scale, rotation (degrees) |
appearance |
opacity |
compositing |
blend, isolate, clip |
flexItem |
alignSelf |
gridItem |
gridColumn, gridRow |
LayoutActor covers what each one does.
layoutGroup
layoutGroup(overrides) builds the layout group with new defaults. Redeclare layout with
it to change an actor’s default box without restating the axes you leave alone:
import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor, function layoutGroup(overrides?: LayoutGroupOverrides): {
x: ActorFieldSignal<LayoutValue>;
y: ActorFieldSignal<LayoutValue>;
width: TypedFieldSignal<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>, number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>>;
height: TypedFieldSignal<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>, number | `${number}%` | Readonly<...>>;
} & SignalGroupDefinition
The canonical `layout` group: x/y/width/height signals with defaults
filled in. Subclasses of `LayoutActor` redeclare `layout = layoutGroup({...})`
to override defaults without restating the untouched axes.layoutGroup } from "@motionactor/core";
class class ThumbnailThumbnail extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor {
Thumbnail.layout: {
x: ActorFieldSignal<LayoutValue>;
y: ActorFieldSignal<LayoutValue>;
width: TypedFieldSignal<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>, number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>>;
height: TypedFieldSignal<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>, number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>>;
} & SignalGroupDefinition
layout = function layoutGroup(overrides?: LayoutGroupOverrides): {
x: ActorFieldSignal<LayoutValue>;
y: ActorFieldSignal<LayoutValue>;
width: TypedFieldSignal<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>, number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>>;
height: TypedFieldSignal<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>, number | `${number}%` | Readonly<...>>;
} & SignalGroupDefinition
The canonical `layout` group: x/y/width/height signals with defaults
filled in. Subclasses of `LayoutActor` redeclare `layout = layoutGroup({...})`
to override defaults without restating the untouched axes.layoutGroup({ LayoutGroupOverrides.width?: LayoutValue | undefinedwidth: 320, LayoutGroupOverrides.height?: LayoutValue | undefinedheight: 180 });
}
const const compiled: CompiledScene<{
thumb: Thumbnail;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled = createScene<{
thumb: Thumbnail;
}, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
thumb: Thumbnail;
}>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene(function* (ctx: SceneContextctx) {
const const thumb: Thumbnailthumb = ctx: SceneContextctx.CompileContext.spawn<Thumbnail>(definition: ActorClass<Thumbnail>, overrides?: {
transform?: {
scale?: SignalInputValue<number> | undefined;
rotation?: SignalInputValue<number> | undefined;
} | undefined;
id?: undefined;
measuredBox?: undefined;
layout?: {
x?: SignalInputValue<LayoutValue> | undefined;
y?: SignalInputValue<LayoutValue> | undefined;
width?: Readonly<{
kind: "layout.percent";
value: number;
}> | SignalInputValue<number | `${number}%` | Readonly<...>> | undefined;
height?: Readonly<...> | ... 1 more ... | undefined;
} | undefined;
... 18 more ...;
parent?: undefined;
} | undefined, options?: {
...;
} | undefined): Thumbnail (+1 overload)
spawn(class ThumbnailThumbnail, { layout?: {
x?: SignalInputValue<LayoutValue> | undefined;
y?: SignalInputValue<LayoutValue> | undefined;
width?: Readonly<{
kind: "layout.percent";
value: number;
}> | SignalInputValue<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>> | undefined;
height?: Readonly<{
kind: "layout.percent";
value: number;
}> | SignalInputValue<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>> | undefined;
} | undefined
layout: { x?: SignalInputValue<LayoutValue> | undefinedx: "50%" } });
return { thumb: Thumbnailthumb };
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
thumb: Thumbnail;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });
const compiled: CompiledScene<{
thumb: Thumbnail;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
thumb: Thumbnail;
}>
at(0).FrameState<{ ...; }>.scene: {
thumb: ResolvedActorOfInstance<Thumbnail> | undefined;
}
scene.thumb: ResolvedActorOfInstance<Thumbnail> | undefinedthumb?.layout: {
x: LayoutValue;
y: LayoutValue;
width: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
height: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
} | undefined
layout;
// { x: "50%", y: 0, width: 320, height: 180 }
A layout value is a number of pixels, a percentage string such as "50%", or pct(50).
layout keeps the value as authored. The resolved box, localLayout and worldLayout, is
always in pixels, with percentages taken of the host’s box or the scene’s viewport.