Tween value-shape contract

The <tween> node and the @roku-sdk/components Tween wrapper animate a field on a target node. Targets and settle candidates take the shape of the animated value, determined by the field and axis. field defaults to "translation"; axis defaults to "all". A target write starts an animation; duration={0} snaps immediately, even when that destination is already armed.

Targets and stops

Target field typeaxisComponents animatedTarget propproposedStops typeExample fields
floatignored; omit it1targetValue: numbernumber[]opacity, width, height, rotation
Vector2D"x"X; Y held at preserveOthertargetValue: numbernumber[] of X coordinatestranslation
Vector2D"y"Y; X held at preserveOthertargetValue: numbernumber[] of Y coordinatestranslation
Vector2D"all"X and YtargetVector: [x, y][number, number][]translation, scale, scaleRotateCenter

preserveOther defaults to 0. A scalar targetValue on a Vector2D field with axis="all" is still accepted and expands to [v, v], for example a uniform scale. This target shorthand does not apply to stops. An explicit targetVector is used verbatim; use targetValue for single-axis motion.

The compatibility implementation recognises the Vector2D fields listed above by name. Custom scalar fields use the float path. For a custom Vector2D field, supply an explicit targetVector and keep that target shape for the lifetime of the tween; custom Vector2D single-axis scalar targets are unsupported by the compatibility implementation. Do not alternate scalar and vector target props to select a value type. Rect2D animation is reserved for future support.

Wrong-shaped stops

Entries are checked individually. A settle logs one skip count when any entries are rejected; valid entries still compete. For known fields, the FlexGrid wrapper also warns once per distinct shape problem in a development build.

SituationBehaviourFlexGrid development warning
Scalar stop on a Vector2D field with axis="all", including a legacy flat [X, Y]Skip each scalar; never lift to [s, s]Yes
Array stop on a Vector2D field with axis="x" or "y"Skip each array, including a pairYes
Array of length other than 2 on a Vector2D field with axis="all"Skip the entryYes
Mixed list, such as [400, [250, 200], 300] with axis="all"Use the valid pair; report its original index, 1Yes
No usable stop remainsNo settle; continue to the tween's existing target without an onAnimationStop settle notificationNo additional warning
axis specified on a float fieldIgnore axisYes
Array stop on a float fieldSkip each array; never project it onto one coordinateYes
Scalar targetValue on a Vector2D field with axis="all"Expand the target to [v, v]No
Custom fieldUse the custom-field rules above; no development shape checkNo

Stops must contain numeric values; non-numeric tuple members are not a supported input.

Settling a vector

Each candidate is a whole [x, y] point. There is no dominant-axis selection of the destination.

  • With one usable candidate, that point is the destination without a direction filter.
  • With several candidates, a point must be strictly ahead on every moving component owned by axis. The nearest eligible point by Euclidean distance over the owned components wins, and all owned components are applied together.
  • For a stationary span, the absolute nearest candidate wins without a direction filter.
  • If valid candidates exist but none is ahead, the original destination is retained and index 0 is reported (it does not identify a selected candidate). This is distinct from having no usable candidates.

The intrinsic's onAnimationStop receives [index, position, stopTime], with stopTime a [second, millisecond] pair. index is the position in proposedStops as written, before filtering. position is a scalar: the owned coordinate for "x" or "y", or the coordinate that travels further for "all" (Y only when its distance is strictly greater). Read proposedStops[index] for the full point when a candidate was selected.

Why scalar stops are not expanded

Expanding a legacy flat [X, Y] list into [X, X] and [Y, Y] invents two diagonal destinations instead of one point. A horizontal release can then settle onto another row. Requiring stops to match the animated value prevents that ambiguity without silently discarding one coordinate. Target shorthand remains useful for uniform scale, but a list of destinations cannot safely use the same shorthand.

Import Tween and TweenProps from @roku-sdk/components. The wrapper accepts targetNode as a node or getter. targetRef remains a deprecated alias for one minor release; targetNode takes precedence. The flex-grid exports forward to components for that same transition period.

If you render the <tween> intrinsic directly, clear targetNode before removing it. The compat shim releases its Animation when a span ends after the node was removed, but a tween removed while idle or paused never reaches that point, so its Animation stays in the parent. The Tween wrapper clears targetNode for you on cleanup.


Did this page help you?