-
Notifications
You must be signed in to change notification settings - Fork 1
Animations
Go-gui's animation system is value-based and frame-driven. Each animation is a small struct
that implements the Animation interface; the framework calls Update every frame until
the animation reports it is stopped. Animations never block the view function — they run
inside the render loop and push value updates into state.
type Animation interface {
ID() string
RefreshKind() AnimationRefreshKind
IsStopped() bool
SetStart(t time.Time)
Update(w *Window, dt float32, ac *AnimationCommands) bool
}Update returns false when the animation has finished; the loop retires it. The
AnimationCommands batch (ac) collects OnValue and OnDone callbacks that fire after
Update returns, preventing re-entrancy into the animation loop.
RefreshKind controls how expensive the next frame is:
| Constant | Effect |
|---|---|
AnimationRefreshNone |
No refresh |
AnimationRefreshRenderOnly |
Repaint without rebuilding layout |
AnimationRefreshLayout |
Full layout rebuild (default for most types) |
Call w.AnimationAdd(a) from any event callback:
a := gui.NewTweenAnimation("my-anim", 0, 300, func(v float32, w *gui.Window) {
gui.State[App](w).PanelWidth = v
})
w.AnimationAdd(a)If an animation with the same ID is already running, AnimationAdd replaces it.
TweenAnimation interpolates a float32 from From to To over a fixed duration using
an easing function.
a := gui.NewTweenAnimation(
"slide-in", // ID
app.PanelX, // from
float32(300), // to
func(v float32, w *gui.Window) {
gui.State[App](w).PanelX = v
},
)
// Optional overrides:
a.Duration = 400 * time.Millisecond
a.Easing = gui.EaseOutBack
a.OnDone = func(w *gui.Window) { /* cleanup */ }
w.AnimationAdd(a)Defaults: 300 ms, EaseOutCubic.
SpringAnimation uses physics-based spring simulation. Unlike Tween, it carries momentum
— if you retarget mid-flight, the element continues from its current velocity.
a := gui.NewSpringAnimation("spring-x", func(v float32, w *gui.Window) {
gui.State[App](w).PanelX = v
})
a.SpringTo(app.PanelX, float32(300))
w.AnimationAdd(a)Retarget during motion (e.g., user dragged while animating):
a.Retarget(newTarget)Spring presets (assign to a.Config):
| Preset | Character |
|---|---|
SpringDefault |
Balanced (stiffness 100, damping 10) |
SpringGentle |
Slow and soft |
SpringBouncy |
High stiffness, low damping |
SpringStiff |
Fast, minimal overshoot |
KeyframeAnimation interpolates through multiple waypoints, each with its own easing.
a := gui.NewKeyframeAnimation(
"bounce-x",
[]gui.Keyframe{
{At: 0, Value: 0, Easing: gui.EaseLinear},
{At: 0.25, Value: 300, Easing: gui.EaseOutCubic},
{At: 0.5, Value: 100, Easing: gui.EaseInOutQuad},
{At: 0.75, Value: 250, Easing: gui.EaseOutBounce},
{At: 1.0, Value: 0, Easing: gui.EaseOutCubic},
},
func(v float32, w *gui.Window) {
gui.State[App](w).BallX = v
},
)
a.Duration = 800 * time.Millisecond
a.Repeat = true // loop indefinitely
w.AnimationAdd(a)At is a normalised time position (0.0–1.0). Each keyframe's Easing controls the curve
into that waypoint from the previous one.
Default duration: 500 ms.
w.AnimateLayout(cfg) records a snapshot of current element positions and then, after the
next layout rebuild, interpolates everything from old positions to new positions. Call it
before the state change that triggers the layout change:
OnClick: func(_ *gui.Layout, e *gui.Event, w *gui.Window) {
w.AnimateLayout(gui.LayoutTransitionCfg{
Duration: 250 * time.Millisecond,
Easing: gui.EaseOutCubic,
})
// Now change state — the layout rebuild will be animated
gui.State[App](w).ItemOrder = reorder(app.ItemOrder)
e.IsHandled = true
},This is the technique known as FLIP (First, Last, Invert, Play). It handles list reorders, accordion expansions, sidebar slides, and any other layout change — without writing per-element animation logic.
HeroTransition morphs a shared element between two different views. Mark the element in
both views with the same HeroID string; when you trigger the transition, the framework
animates the element from its position in the outgoing view to its position in the incoming
view.
// Trigger before switching views:
h := gui.NewHeroTransition(gui.HeroTransitionCfg{
Duration: 300 * time.Millisecond,
Easing: gui.EaseOutCubic,
})
w.AnimationAdd(h)
gui.State[App](w).ActiveView = "detail"Only one HeroTransition can be active at a time; a second call replaces the first.
EasingFn is func(float32) float32. All built-ins:
| Function | Curve |
|---|---|
EaseLinear |
Constant speed |
EaseInQuad / EaseOutQuad / EaseInOutQuad
|
Quadratic |
EaseInCubic / EaseOutCubic / EaseInOutCubic
|
Cubic |
EaseInBack / EaseOutBack
|
Overshoot |
EaseOutElastic |
Elastic spring-back |
EaseOutBounce |
Bouncing ball |
EaseCSS / EaseInCSS / EaseOutCSS / EaseInOutCSS
|
CSS standard curves |
CubicBezier(x1, y1, x2, y2) |
Custom cubic Bézier |
Custom easing is any func(float32) float32:
a.Easing = func(t float32) float32 {
return t * t * (3 - 2*t) // smoothstep
}Getting Started
Widgets
Layout & Interaction
Visuals
Reference