Animations
Motion in bibui is part of the build, like everything else. There are no animation objects to start or stop. You ask the runtime where a value has got to on its way to a target, use the answer to size or colour a box, and the runtime keeps asking for frames until everything has arrived. Every demo on this page is bibui running in your browser.
Two rules hold throughout. An eased value steps once, at the end of the frame, so a build that runs twice for the same input gets the same answer both times. And while anything is still moving, the window's BeginFrame keeps returning. When everything has arrived, the loop goes idle and costs nothing.
Hover and press
A retained box that claims a press carries two eased values. HotT goes towards 1 while the pointer is over the box, at the theme's hover speed, and back to 0 when it leaves. ActiveT jumps to 1 on a press, because a press that fades in under the finger feels slow, and eases back on release. Both are written only when the box asks, so reading HotT is how a box opts in to a hover look.
Three cards whose fill, lift and underline follow HotT and ActiveT. This demo runs bibui in the browser and needs JavaScript and WebAssembly.
Box card = runtime.BoxFromId(title, InteractionKinds.LeftPress);
runtime.InteractionsFor(card); // the ask that keeps HotT and ActiveT moving
card.Sized(Size.Px(150f), Size.Px(96f));
card.Style.BackgroundColor = UiColor.Accent.WithAlpha(0.05f + 0.2f * card.HotT + 0.15f * card.ActiveT);
card.Style.Translate = new Vector2(0f, -6f * card.HotT + 3f * card.ActiveT);For the common case there is no arithmetic to write: Style.HoverBackgroundColor, DrawFlags.HotEffects and DrawFlags.ActiveEffects are blended by HotT and ActiveT when the box is painted, which is how the built-in components do it.
runtime.Animate
For anything else that should glide, runtime.Animate(id, property, target) returns where the value has got to. The first time a value is asked for it starts at its target, so a screen does not slide into place on the frame it appears. The speed is in halvings per second, so the value closes half the remaining gap every 1/speed seconds at any frame rate. It defaults to the theme's Motion.Default.
A panel whose width is runtime.Animate's answer, opened and closed by a button. This demo runs bibui in the browser and needs JavaScript and WebAssembly.
if (runtime.Button("panel-toggle", _open ? CloseLabel : OpenLabel).Clicked) _open = !_open;
float open = runtime.Animate("panel", "width", _open ? 1f : 0f);
Box side = runtime.Box().Sized(Size.Px(40f + 160f * open), Size.Px(120f)); // the size is the eased value
Text label = runtime.Text(PanelLabel, ComponentSize.Sm);
label.Style.Opacity = open;A value nobody asks for in a frame stops moving and is forgotten soon after. There is nothing to register or release.
Transitions: timed and springs
Exponential approach suits a hover. For motion with a start and an end, give Animate a Transition. Transition.Timed takes a duration and a curve, and Transition.OnSpring a spring that keeps its speed when the target changes mid-flight. Press the button twice quickly to see the difference.
Three dots travelling to the same target: exponential, timed with an emphasized curve, and a bouncy spring. This demo runs bibui in the browser and needs JavaScript and WebAssembly.
float exp = runtime.Animate("t-exp", "x", target);
float timed = runtime.Animate("t-timed", "x", target, Transition.Timed(0.6f, Easing.Emphasized));
float spring = runtime.Animate("t-spring", "x", target, Transition.OnSpring(Spring.Bouncy));
Box dot = runtime.Box().Sized(Size.Px(20f), Size.Px(20f)).At(4f + 292f * spring, 4f);| Ready-made | What it is |
|---|---|
Easing | Linear, Ease, EaseIn, EaseOut, EaseInOut, Standard, Emphasized, EmphasizedAccelerate, or any cubic-bezier |
Spring | Snappy, Gentle, Bouncy, or Spring.FromResponse(seconds, dampingRatio) to make one by feel |
Transition | Exponential(speed), Timed(seconds, easing, delay), OnSpring(spring, delay), Instant |
Animate also takes a Vector2, and runtime.AnimateColor(id, property, colour) crossfades a colour in Oklab. A theme token stays a token, so the colour follows a theme swap.
Presence: entering and leaving
Something that must stay on screen while it leaves asks runtime.Presence(id, present) every frame, present or not, and builds while it is Mounted. Progress runs from 0 to 1 on the way in and back on the way out, and PresenceStyle turns it into a fade, a slide or a pop in one line.
PresenceList does the same for the rows of a list. A removed row stays in place, with its item, while it collapses, and added rows can stagger in. CollapseBox gives each row the height its presence says.
A list whose rows slide in when added and collapse when clicked. This demo runs bibui in the browser and needs JavaScript and WebAssembly.
foreach (var row in runtime.PresenceList("rows", _rows, KeyOf, stagger: 0.03f))
{
Box holder = runtime.BeginParent(runtime.CollapseBox("hold-" + row.Key, row.Progress).Sized(Size.Fill));
Box line = runtime.BoxFromId("row-" + row.Key,
row.Exiting ? InteractionKinds.None : InteractionKinds.LeftPress);
if (runtime.InteractionsFor(line).Clicked && !row.Exiting) remove = row.Item;
line.Sized(Size.Fill, Size.Px(40f));
PresenceStyle.SlideFromRight(32f).ApplyTo(line.Style, row.Progress);
// … the row's text
runtime.EndParent(holder);
}A leaving row claims nothing, so a second click on it reaches nothing. Act only on what is not Exiting.
Transforms
Style.Translate, Scale, Rotate and TransformOrigin move a node and its subtree when it is painted, not when it is laid out. Nothing around it moves, and the pointer finds it where it is drawn. The cards in the first demo lift this way. Driving a transform from an eased value is the cheapest motion there is: the layout is unchanged, and only the painted rectangles differ.
Crossfading a theme
runtime.SetTheme(theme, transition) travels every colour from the old theme to the new one, components and your own boxes alike, because they hold tokens rather than colours.
A switch that crossfades the whole demo between bibui-dark and bibui-light. This demo runs bibui in the browser and needs JavaScript and WebAssembly.
if (runtime.Switch("theme-light", _light, Light).Clicked)
{
_light = !_light;
runtime.SetTheme(_light ? ThemeLoader.BibuiLight() : ThemeLoader.BibuiDark(),
Transition.Timed(0.45f, Easing.Standard));
}Reduced motion
runtime.ReducedMotion, the environment variable BIBUI_REDUCED_MOTION=1, or a theme with Motion.Easing = MotionEasing.None makes every eased value arrive in one step, hover and the renderer's own values included. Components read their speeds from runtime.Rates, which are all 1 under reduced motion, so nothing else has to check.
Rates.Toggle: a control crossing between two states.Rates.Hover: hover, which the runtime already uses forHotT.Rates.Follow: a position chasing input, such as scrolling and the caret.Rates.Default: anything else, and whatAnimateuses when you pass no rate.
Motion that starts outside a build
If your state changes outside a build, from a timer or a network reply, call runtime.RequestFrame(). It is safe from any thread and wakes a window that is waiting for input. The next build reads the new state, and any Animate it asks starts travelling from there.