docs v1.0.0

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.

Live demo
Runs in your browser

Three cards whose fill, lift and underline follow HotT and ActiveT. This demo runs bibui in the browser and needs JavaScript and WebAssembly.

C#
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.

Live demo
Runs in your browser

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.

C#
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.

Live demo
Runs in your browser

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.

C#
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-madeWhat it is
EasingLinear, Ease, EaseIn, EaseOut, EaseInOut, Standard, Emphasized, EmphasizedAccelerate, or any cubic-bezier
SpringSnappy, Gentle, Bouncy, or Spring.FromResponse(seconds, dampingRatio) to make one by feel
TransitionExponential(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.

Live demo
Runs in your browser

A list whose rows slide in when added and collapse when clicked. This demo runs bibui in the browser and needs JavaScript and WebAssembly.

C#
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.

Live demo
Runs in your browser

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.

C#
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 for HotT.
  • Rates.Follow: a position chasing input, such as scrolling and the caret.
  • Rates.Default: anything else, and what Animate uses 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.