Layout animation
Make slots and layers move and resize smoothly whenever the layout changes, with Animate, and no animation state in your model.
When a panel opens, a list re-sorts, or a split changes ratio, the layout jumps to its new shape on the next frame.
Add .Animate() to a slot or layer and bloom glides it there instead, reflowing its content at each size along
the way. Your model only holds the application state, such as whether the sidebar is open, and no animation
values.
width := 0
if m.sidebarOpen {
width = 24
}
bloom.Row(
bloom.Fixed(width, sidebar).ID("sidebar").Animate(),
bloom.Fill(content).ID("content").Animate(),
)Toggle m.sidebarOpen and the sidebar slides open or closed while the content resizes to match.
The reflow example animates a sidebar, a re-sorting list, a changing split, and a panel coming and going this way:

Requirements
Layout animation is kept by the *bloom.Root between frames, so it needs three things:
- An ID on each animated slot or layer, so bloom can match it from one frame to the next.
- A
Root, rendering withm.ui.View.bloom.Renderandbloom.Solvedon't keep state between calls, and always show the final layout. - Every message passed to
m.ui.Update, with its command returned. That's what schedules frames while anything is moving:
func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmd := m.ui.Update(msg)
// ...
return m, cmd
}What animates
- A slot moving to a new place, such as list items re-sorting, glides there. Give each item an ID that stays with the item, not with its position, like its name or database ID.
- A slot changing size grows or shrinks, and its children lay out again at each intermediate size.
- A slot whose size becomes zero, like the closed sidebar above, animates closed rather than vanishing.
- A layer anchored to a node, such as a marker anchored to the selected row, follows it smoothly from row to row.
Some changes happen at once: a collapsed slot, a node removed from the tree, and a layer whose anchor disappears are gone on the next frame.
Choose the motion
Animate takes the same options as an animated value. The default eases over 250
milliseconds; a spring suits things that move often, because it keeps its momentum when the target changes
mid-flight:
bloom.Fixed(width, sidebar).ID("sidebar").Animate(anim.Spring(16, 0.85))
bloom.Layer(marker).
AnchorTo(selected, bloom.Right).
ID("marker").
Animate(anim.Spring(20, 1))The options are captured when the ID first appears, so changing them later has no effect on a node already on
screen. The frame rate and clock come from the root, set with
bloom.WithFPS,
bloom.WithUnfocusedFPS, and
bloom.WithClock.
Geometry while animating
m.ui.Frame().Rect(id) reports where a node was drawn on screen, so mid-animation it's the in-between
rectangle. bloom.Solve reports where the layout is headed. Use Solve when you need final sizes in Update,
such as to size a Bubbles component.
When to use a Value instead
Animate covers layout: position and size. Use an anim.Value when you need the
number in your model, such as to drive an effect, to read the progress, or to animate something that isn't a
rectangle.
Related
SlotNode.AnimateandLayerNode.AnimateAPI reference- The
reflowexample: a sidebar, a re-sorting list, a changing split, a panel coming and going, and a marker following the selection
Last updated on
Transitions
Animate nodes and layers in and out with effects, combine and sequence effects, and open a dialog from the button that was pressed.
Feedback effects
Shake rejected input, show skeleton placeholders that shimmer while loading, roll changing numbers, burst confetti, glitch a badge, spotlight a tour step, and scroll text that doesn't fit.