motion

package
v0.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 31, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package motion provides enter/exit/transition primitives for animating widgets into, out of, and between visual states.

Each primitive composes pulse/tween (deterministic, frame-indexed opacity) and pulse/spring (physics-driven scale): opacity provides predictable timing, spring provides physical character. The two run on independent timescales — opacity finishes at frame Frames, scale settles when the spring's restoring force balances out.

Composition with widgets

Apply is the bridge from a State to a Gio layout.Widget. It records the widget once (to obtain its layout footprint), then replays the recorded ops inside an op.Affine scale-around-centre transform and a paint.PushOpacity layer. The widget is laid out exactly once; the visual transformation is purely a paint-time effect.

enter := motion.NewEnter(motion.Options{})
for !enter.Settled(0.005) {
    enter.Tick(60)            // 60 Hz frame loop
    motion.Apply(gtx, enter.State(), buttonWidget)
}

Variant pattern

Per DESIGN §"Phase 3 — Pulse" composition mechanism, Pulse exposes motion-aware *variants* of Prism components by wrapping their render output with Apply. A motion-animated button is just:

bw := button.Render(shaper, label, colors, sp, rad, ts, btnState)
motion.Apply(gtx, primitive.State(), bw)

Exporting wrapper functions per Prism component is G3.6's job; this package ships only the primitives.

Index

Constants

View Source
const (
	// DefaultFrames is the opacity-tween duration, in frames.
	DefaultFrames = 30

	// DefaultFromScale is the scale a widget starts at on Enter (and
	// returns to on Exit).
	DefaultFromScale = 0.85
)

Defaults applied when a corresponding Options field is zero-valued.

Variables

View Source
var DefaultSpring = spring.Options{
	Stiffness: defaultSpringStiffness,
	Damping:   2 * math.Sqrt(defaultSpringStiffness*defaultSpringMass),
	Mass:      defaultSpringMass,
}

DefaultSpring is the spring.Options used when Options.Spring is zero-valued. Critically damped at k=80, m=1 — a brisk, no-overshoot curve that settles in ~30 frames at invDt=60 (matching DefaultFrames).

View Source
var Hidden = State{Opacity: 0, Scale: DefaultFromScale}

Hidden is the canonical end-state of an Exit and the starting state of an Enter: fully transparent, scaled down to DefaultFromScale.

View Source
var Visible = State{Opacity: 1, Scale: 1}

Visible is the canonical end-state of an Enter and the starting state of an Exit: fully opaque, identity scale.

Functions

func Apply

Apply renders w with the visual transformation s applied: an op.Affine scale around the widget's centre and a paint.PushOpacity layer. Returns w's natural layout dimensions (the visual scale does not change the widget's footprint).

w is laid out exactly once: Apply records w into a macro to obtain its dimensions, then replays the macro inside the transform/opacity stack. Dimensions therefore stay stable across an animation, so surrounding layout does not jitter.

Apply does not short-circuit on Opacity == 0 — the widget is still laid out and its ops are still recorded so the returned dimensions remain the true widget footprint at every frame.

Types

type Enter

type Enter struct {
	// contains filtered or unexported fields
}

Enter animates a widget from Hidden to Visible. Construct with NewEnter, advance with Enter.Tick, query with Enter.State or Enter.Settled.

func NewEnter

func NewEnter(opts Options) *Enter

NewEnter constructs an Enter primitive with opts.

func (*Enter) Frame

func (e *Enter) Frame() int

Frame returns the current frame index (0 before any Enter.Tick).

func (*Enter) Settled

func (e *Enter) Settled(tol float64) bool

Settled reports whether the animation has reached Visible within tolerance: the opacity tween has finished AND the scale spring is at rest at 1.0.

func (*Enter) State

func (e *Enter) State() State

State returns the current visual transformation.

func (*Enter) Tick

func (e *Enter) Tick(invDt float64)

Tick advances the animation by one simulation step. invDt mirrors spring.Spring.Tick: the DESIGN-recommended frame loop convention is max(1, fps/30).

type Exit

type Exit struct {
	// contains filtered or unexported fields
}

Exit animates a widget from Visible to Hidden. Mirrors Enter.

func NewExit

func NewExit(opts Options) *Exit

NewExit constructs an Exit primitive with opts.

func (*Exit) Frame

func (e *Exit) Frame() int

Frame returns the current frame index.

func (*Exit) Settled

func (e *Exit) Settled(tol float64) bool

Settled reports whether the animation has reached Hidden within tolerance: opacity finished AND scale spring at rest at FromScale.

func (*Exit) State

func (e *Exit) State() State

State returns the current visual transformation.

func (*Exit) Tick

func (e *Exit) Tick(invDt float64)

Tick advances the animation by one simulation step.

type Options

type Options struct {
	// Frames is the opacity-tween duration in frames. Zero defaults to
	// [DefaultFrames]. The scale spring runs alongside on its own
	// physical timescale.
	Frames int

	// Spring is the [spring.Options] for the scale animation. The zero
	// value (all fields zero) is replaced with [DefaultSpring]; pass any
	// non-zero field to override individually via [spring.New]'s own
	// per-field defaulting.
	Spring spring.Options

	// FromScale is the scale at the Hidden end of the animation. Zero
	// defaults to [DefaultFromScale].
	FromScale float64
}

Options configures a single motion primitive. Zero-valued fields are replaced with package defaults at construction time, so the zero Options value produces a working primitive with the canonical feel.

type State

type State struct {
	// Opacity in [0,1]: 0 = fully transparent, 1 = fully opaque. Drives
	// a [paint.PushOpacity] layer in [Apply].
	Opacity float64

	// Scale factor: 1.0 = identity. Drives an [op.Affine] scale around
	// the widget's centre in [Apply], so 0.85 shrinks the widget toward
	// its midpoint without changing its layout footprint.
	Scale float64
}

State captures the visual transformation applied to a widget at one instant of an animation. Both fields are normalised: Opacity in [0,1], Scale as a multiplier (1.0 = identity).

type Transition

type Transition struct {
	// contains filtered or unexported fields
}

Transition cross-fades between two widgets — an outgoing widget that runs an Exit and an incoming widget that runs an Enter in parallel, both ticked together.

func NewTransition

func NewTransition(opts Options) *Transition

NewTransition constructs a Transition with opts. Both Exit and Enter halves use the same opts.

func (*Transition) Frame

func (t *Transition) Frame() int

Frame returns the current frame index (incremented once per Transition.Tick).

func (*Transition) In

func (t *Transition) In() State

In returns the incoming widget's current state.

func (*Transition) Out

func (t *Transition) Out() State

Out returns the outgoing widget's current state.

func (*Transition) Settled

func (t *Transition) Settled(tol float64) bool

Settled reports whether both halves have reached their respective resting states within tolerance.

func (*Transition) Tick

func (t *Transition) Tick(invDt float64)

Tick advances both halves by one simulation step.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL