Animation Example
Live Demo
Overview
This example demonstrates vgui’s animation and transition support:
animate-pulse/animate-bounce/animate-pingkeyframe animations.transition-opacityandtransition-colorswithduration-*/ease-*timing.- Custom
animate={...}attribute for user-defined keyframe animations.
Source Code
#![cfg_attr(target_family = "wasm", no_main)]
use std::time::Duration;
use gpui::{px, size, App, Bounds, WindowBounds, WindowOptions, AnimationExt};
use vgui::prelude::*;
#[cfg(not(target_family = "wasm"))]
use gpui_platform::application;
#[cfg(target_family = "wasm")]
use gpui_platform::single_threaded_web;
fn app() -> impl gpui::IntoElement {
view! {
<div class="flex flex-col gap-6 p-6 bg-[#1a1a2e] w-full h-full text-white overflow-y-auto">
<h2 class="text-lg font-bold">{"Animations & Transitions"}</h2>
// ── Built-in keyframe animations ───────────────────────────
<div class="flex flex-col gap-2">
<span class="text-sm text-[#aaa]">{"animate-pulse"}</span>
<div class="bg-[#3b82f6] rounded p-4 animate-pulse">
{"Pulsing"}
</div>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-[#aaa]">{"animate-bounce"}</span>
<div class="bg-[#10b981] rounded p-4 animate-bounce">
{"Bouncing"}
</div>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-[#aaa]">{"animate-ping"}</span>
<div class="bg-[#ef4444] rounded p-4 animate-ping">
{"Pinging"}
</div>
</div>
// ── Transitions on hover ───────────────────────────────────
<div class="flex flex-col gap-2">
<span class="text-sm text-[#aaa]">{"transition-opacity hover:opacity-50 duration-300"}</span>
<button class="bg-[#6366f1] hover:opacity-50 rounded p-3 transition-opacity duration-300">
{"Fade on hover"}
</button>
</div>
<div class="flex flex-col gap-2">
<span class="text-sm text-[#aaa]">{"transition-colors hover:bg-[#2563eb] duration-300"}</span>
<button class="bg-[#6366f1] hover:bg-[#2563eb] rounded p-3 transition-colors duration-300 ease-in-out">
{"Color on hover"}
</button>
</div>
// ── Custom animation via animate={...} ─────────────────────
<div class="flex flex-col gap-2">
<span class="text-sm text-[#aaa]">{"animate={...} custom"}</span>
<div
class="bg-[#f59e0b] rounded p-4"
animate={|el| el.with_animation(
"custom-breath",
gpui::Animation::new(Duration::from_millis(1500))
.repeat()
.with_easing(gpui::ease_in_out),
|mut el, delta| {
el = el.opacity(0.4 + 0.6 * (delta * std::f32::consts::PI).sin());
el
},
)}
>
{"Custom breathing"}
</div>
</div>
</div>
}
}
fn run() {
#[cfg(not(target_family = "wasm"))]
let gpui_app = application();
#[cfg(target_family = "wasm")]
let gpui_app = single_threaded_web();
let launch = |cx: &mut App| {
let bounds = Bounds::centered(None, size(px(520.), px(640.0)), cx);
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
..Default::default()
},
|window, cx| vgui::mount(window, cx, app),
)
.unwrap();
};
#[cfg(not(target_family = "wasm"))]
gpui_app.run(launch);
#[cfg(target_family = "wasm")]
std::mem::forget(gpui_app.run_embedded(launch));
}
#[cfg(not(target_family = "wasm"))]
fn main() {
run();
}
#[cfg(target_family = "wasm")]
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn start() {
gpui_platform::web_init();
vgui::intercept_keyboard_events();
run();
}
Key Concepts
Built-in animations
animate-pulse, animate-bounce, and animate-ping are Tailwind-compatible
classes parsed at compile time by tw!. Each maps to a gpui Animation with a
repeating loop and a custom animator closure.
Transitions
transition-opacity / transition-colors animate between the base style and the
hover: style when the pointer enters or leaves the element. The view! macro
creates a hover signal, registers on_hover, and calls apply_transition to
interpolate the relevant properties.
Custom animate={...}
The animate attribute accepts a closure |el| -> AnimationElement<E>. This
gives full access to gpui’s with_animation API for custom keyframes, durations,
easings, and repeat behavior.
Running
Native:
cargo run -p vgui-animation
Web (WASM):
# Build the WASM binary
cargo +nightly build --target wasm32-unknown-unknown -p vgui-animation --release
# Generate JS bindings
wasm-bindgen --target web --out-dir examples/animation/dist \
--no-typescript target/wasm32-unknown-unknown/release/animation.wasm
# Serve and open in a browser
python3 scripts/serve_plain.py 8080 examples/animation