Skip to content
MotionActor
Esc
↑↓navigate↵open⌘Jpreview
On this page

ComponentActor

An actor whose only state is a React function, from @motionactor/primitives. It draws from the frame number.

ComponentActor holds a render function as a signal. It has a box like any LayoutActor, so a scene can spawn it, move it and fade it, and the DOM backend calls the function on every frame with the frame number and the box size.

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 ComponentActorComponentActor } from "@motionactor/primitives"; const const Ticker: typeof ComponentActorTicker = class ComponentActorComponentActor.ComponentActor.withRender(render: ComponentActorRender): typeof ComponentActorwithRender(({ frame: numberframe, fps: numberfps, width: numberwidth }) => ( <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.style?: React.CSSProperties | undefinedstyle={{ StandardLonghandProperties<string | number, string & {}>.width?: Property.Width<string | number> | undefined
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015. **Syntax**: `auto | <length-percentage [0,∞]> | min-content | max-content | fit-content | fit-content(<length-percentage [0,∞]>) | <calc-size()> | <anchor-size()>` **Initial value**: `auto` | Chrome | Firefox | Safari | Edge | IE | | :----: | :-----: | :----: | :----: | :---: | | **1** | **1** | **1** | **12** | **4** |
@seehttps://developer.mozilla.org/docs/Web/CSS/Reference/Properties/width
width
, StandardLonghandProperties<string | number, string & {}>.fontVariantNumeric?: Property.FontVariantNumeric | undefined
This feature is well established and works across many devices and browser versions. It’s been available across browsers since January 2020. **Syntax**: `normal | [ <numeric-figure-values> || <numeric-spacing-values> || <numeric-fraction-values> || ordinal || slashed-zero ]` **Initial value**: `normal` | Chrome | Firefox | Safari | Edge | IE | | :----: | :-----: | :-----: | :----: | :-: | | **52** | **34** | **9.1** | **79** | No |
@seehttps://developer.mozilla.org/docs/Web/CSS/Reference/Properties/font-variant-numeric
fontVariantNumeric
: "tabular-nums" }}>
{(frame: numberframe / fps: numberfps).Number.toFixed(fractionDigits?: number): string
Returns a string representing a number in fixed-point notation.
@paramfractionDigits Number of digits after the decimal point. Must be in the range 0 - 20, inclusive.
toFixed
(2)}s
</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> )); createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, 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 ticker: ComponentActorticker = ctx: SceneContextctx.
CompileContext.spawn<ComponentActor>(definition: ActorClass<ComponentActor>, overrides?: {
    layout?: {
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>> | undefined;
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        height?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<...> | undefined;
    } | undefined;
    ... 23 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): ComponentActor (+1 overload)
spawn
(const Ticker: typeof ComponentActorTicker, {
layout?: {
    width?: Readonly<{
        kind: "layout.percent";
        value: number;
    }> | SignalInputValue<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>> | undefined;
    x?: SignalInputValue<LayoutValue> | undefined;
    y?: SignalInputValue<LayoutValue> | undefined;
    height?: Readonly<{
        kind: "layout.percent";
        value: number;
    }> | SignalInputValue<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>> | undefined;
} | undefined
layout
: { x?: SignalInputValue<LayoutValue> | undefinedx: 40, y?: SignalInputValue<LayoutValue> | undefinedy: 40,
width?: Readonly<{
    kind: "layout.percent";
    value: number;
}> | SignalInputValue<number | `${number}%` | Readonly<{
    kind: "layout.percent";
    value: number;
}>> | undefined
width
: 240,
height?: Readonly<{
    kind: "layout.percent";
    value: number;
}> | SignalInputValue<number | `${number}%` | Readonly<{
    kind: "layout.percent";
    value: number;
}>> | undefined
height
: 60 },
}); yield* const ticker: ComponentActorticker.
LayoutActor<{}>.appearance: {
    opacity: TypedFieldSignal<number, number>;
} & SignalGroupDefinition
appearance
.opacity: TypedFieldSignal<number, number>opacity.function tween(target: number, options: number | SignalTweenOptions<number>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(0.4, 1);
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });

withRender returns a subclass with that function as its default, so each use gets its own class. Without it, pass the function as the render override when spawning.

The function receives frame, fps, width and height, plus durationInFrames when the host knows it. It has no access to the actor’s signals, because render is the only one.

Swapping the view

*setRender(render, duration = 0) replaces the function at the cursor and then waits duration seconds:

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 ComponentActorComponentActor } from "@motionactor/primitives"; createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, 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 panel: ComponentActorpanel = ctx: SceneContextctx.
CompileContext.spawn<ComponentActor>(definition: ActorClass<ComponentActor>, overrides?: {
    measuredBox?: undefined;
    render?: SignalInputValue<ComponentActorRender | null> | undefined;
    setRender?: undefined;
    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<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 20 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): ComponentActor (+1 overload)
spawn
(class ComponentActorComponentActor, {
render?: SignalInputValue<ComponentActorRender | null> | undefinedrender: () => <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Loading</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>, }); yield* const panel: ComponentActorpanel.ComponentActor.setRender(render: ComponentActorRender, duration?: Duration): Generator<number, void, unknown>
Swap the view, then hold it for `duration` seconds.
setRender
(() => <React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>Ready</React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>p>, 2);
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });

Limits

ComponentActor sets static frameDriven = true, because its function may draw anything from frame. Export therefore renders every one of its frames and never reuses a frame that looks unchanged. See Capture and export.

Its renderer is DOM only. On a Pixi or Three.js layer it draws nothing.

It suits a one-off surface or a sketch of something that has no actor yet. Once the drawing depends on values the scene should animate, such as a progress number or a label, those values belong in signals on an actor of its own, with a renderer that reads them. Otherwise the timeline cannot tween them, and an editor cannot show them.

Was this page helpful?