Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Animation

The animation system lives in waterui_core::animation and is wired into every Signal via the AnimationExt trait. Instead of imperatively driving tweens, you attach animation metadata to the binding that powers your view, and the renderer interpolates between old and new values.

Animated Bindings

#![allow(unused)]
fn main() {
use waterui::prelude::*;
use waterui::reactive::binding;
use waterui_core::{animation::Animation, AnimationExt};
use core::time::Duration;

pub fn fading_badge() -> impl View {
    let visible = binding(true);
    let opacity = visible
        .map(|flag| if flag { 1.0 } else { 0.0 })
        .with_animation(Animation::ease_in_out(Duration::from_millis(250)));

    text!("Opacity: {opacity:.0}")
}
}
  • .animated() applies the platform-default animation.
  • .with_animation(Animation::Linear(..)) lets you choose easing.
  • .with_animation(Animation::spring(stiffness, damping)) yields physically based motion.

When the binding changes, the animation metadata travels with it through map, zip, or any other combinator.

Coordinated Transitions

Attach animation metadata to multiple bindings and update them together:

#![allow(unused)]
fn main() {
let offset = binding((0.0_f32, 0.0_f32));
let font_size = binding(14.0_f32);

let offset = offset.with_animation(Animation::spring(200.0, 15.0));
let font_size = font_size.animated();

vstack((text!("offset: {offset:?}, size: {font_size}"),))
}

Calling offset.set((0.0, 50.0)) and opacity.set(1.0) triggers both animations concurrently.

Animation Hooks

Renderers look for an Animation in the view metadata. If your app needs global animation policy (reduced motion, slower transitions), install a hook:

#![allow(unused)]
fn main() {
use waterui_core::env::Environment;
use waterui_core::{animation::Animation, view::Hook};

pub fn install_reduced_motion(env: &mut Environment) {
    env.insert_hook(|_, config: Animation| Animation::EaseOut(Duration::from_millis(100)));
}
}

Any binding that calls .animated() now receives the shorter ease-out curve.

Testing and Debugging

  • Run with the WATERUI_ANIMATION=off environment variable (or a custom hook) to disable animations during snapshot testing.
  • When a view fails to animate, ensure the binding changed (animations only run when the value differs) and that you applied the animation to the reactive value, not the literal view.

Animations are declarative in WaterUI—keep state updates pure and describe how values should transition. The runtime handles frame-by-frame interpolation on every platform.