Transitions
A transition eases a property to each new value over a fixed time, with an
easing curve. It is declared on the value itself, with .transition(..).
Declaring one
extern crate guido;
use guido::prelude::*;
fn main() {
let value = Color::WHITE;
let timing = TimingFunction::EaseOut;
let duration_ms = 200.0;
let _: Animated<Color> =
value.transition(Transition::new(duration_ms, timing))
;
}
duration_ms- Animation duration in millisecondstiming- Easing curve for the animation
A bare number is the short form: .transition(200.0) is 200ms eased out.
Examples
Background Animation
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.background(Color::rgb(0.3, 0.5, 0.8).transition(200.0))
.when_hovered(|s| s.lighter(0.15))
;
}
Border and Outline Animation
A border is declared as a pair and each half carries its own timing. Its width takes room inside the box, so a width that animates moves the content with it; a hover ring is an outline, which takes none, and whose three halves animate the same way:
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.border(1.0, Color::rgb(0.3, 0.3, 0.4).transition(150.0))
.outline(2.0, Color::TRANSPARENT.transition(150.0), 2.0)
.when_hovered(|s| s
.border(1.0, Color::rgb(0.5, 0.5, 0.6))
.outline(2.0, Color::rgb(0.5, 0.5, 0.6), 2.0))
;
}
Transform Animation
extern crate guido;
use guido::prelude::*;
fn main() {
container()
.scale(Scale::NONE.transition(Transition::new(300.0, TimingFunction::EaseOut)))
.when_pressed(|s| s.scale(0.98))
;
}
The resting value is what the property is when no state layer overrides it —
here Scale::NONE, the size the press shrinks away from.
Duration Guidelines
| Duration | Use Case |
|---|---|
| 100-150ms | Quick feedback (button press) |
| 150-200ms | State changes (hover) |
| 200-300ms | Content changes (expand/collapse) |
| 300-500ms | Major transitions (page changes) |
Combining with State Layers
A state layer supplies a value for a property somebody else declared, and carries no timing of its own. The property eases whatever supplies its value:
extern crate guido;
use guido::prelude::*;
const FLAT: Shadow = Shadow::none();
const LOW: Shadow = Shadow::simple((0.0, 1.0), 3.0, Color::rgba(0.0, 0.0, 0.0, 0.12));
const RAISED: Shadow = Shadow::simple((0.0, 3.0), 6.0, Color::rgba(0.0, 0.0, 0.0, 0.19));
fn main() {
container()
.background(Color::rgb(0.2, 0.2, 0.3).transition(200.0))
.shadow(LOW.transition(200.0))
// State changes trigger the animations declared above
.when_hovered(|s| s.lighter(0.1).shadow(RAISED))
.when_pressed(|s| s.darker(0.05).shadow(FLAT))
;
}
Writing a timing on the override does not compile — it would be a declaration that is legal and does nothing:
// not compiled: the line is the mistake the paragraph above names.
.when_hovered(|s| s.background(HOT.transition(900.0))) // error
Complete Example
extern crate guido;
use guido::prelude::*;
const LOW: Shadow = Shadow::simple((0.0, 1.0), 3.0, Color::rgba(0.0, 0.0, 0.0, 0.12));
const RAISED: Shadow = Shadow::simple((0.0, 3.0), 6.0, Color::rgba(0.0, 0.0, 0.0, 0.19));
const HIGH: Shadow = Shadow::simple((0.0, 6.0), 10.0, Color::rgba(0.0, 0.0, 0.0, 0.22));
fn animated_card() -> Container {
container()
.padding(20.0)
.background(Color::rgb(0.15, 0.15, 0.2).transition(200.0))
.corners(12.0)
.border(1.0, Color::rgb(0.25, 0.25, 0.3))
.outline(2.0, Color::TRANSPARENT.transition(150.0), 2.0)
.shadow(RAISED.transition(250.0))
// State layers
.when_hovered(|s| s
.lighter(0.05)
.outline(2.0, Color::rgb(0.4, 0.6, 0.9), 2.0)
.shadow(HIGH)
)
.when_pressed(|s| s
.darker(0.02)
.shadow(LOW)
)
.child(container().child(text("Hover me!").color(Color::WHITE)))
}
fn main() {}
API Reference
/// Ease to each new value this holds
value.transition(transition: impl Into<TransitionConfig>) -> Animated<T>;
/// A bare number is milliseconds, eased out
Transition::new(duration_ms: f32, timing: TimingFunction) -> Transition;
/// Create a spring-based transition
Transition::spring(config: SpringConfig) -> Transition