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

Transitions

Transitions occur when you have a conditional view like if_view! or match_view! which changes branches. Because the branches contain different subtrees, there is no reasonable way to animate between them. In cases like this, the conditional view uses a transition to animate between the two branches.

Transition

The properties of views within unchanged branches are still animated as normal.

Configuring Transitions

The Opacity transition is used by default, but it can be changed with the .transition() modifier.

#![allow(unused)]
fn main() {
extern crate buoyant;
extern crate embedded_graphics;
use embedded_graphics::pixelcolor::Rgb888;
use std::time::Duration;
use buoyant::{
    if_view,
    view::prelude::*,
    transition::{Move, Slide},
};

fn maybe_round(is_square: bool) -> impl View<Rgb888, ()> {
    if_view!((is_square) {
        Rectangle
          .frame_sized(20, 20)
          .transition(Slide::leading())
    } else {
        Circle
          .frame_sized(20, 20)
          .transition(Move::top())
    })
    .animated(Animation::ease_out(Duration::from_millis(120)), is_square)
}
}

Transitions must have some parent animation node with a value that matches the condition for the transition or they will not animate. The transition duration is determined by the animation node driving it.

For transitions like Move and Slide, the size of the whole transitioning subtree is used to determine how far to move the view. The transition modifier does not need to be the outermost modifier, and where you place it has no bearing on the resulting effect.

These two views produce the exact same transition:

#![allow(unused)]
fn main() {
extern crate buoyant;
extern crate embedded_graphics;
use embedded_graphics::pixelcolor::Rgb888;
use std::time::Duration;
use buoyant::{
    if_view,
    view::prelude::*,
    transition::{Move, Slide},
};

fn all_the_same_1(is_square: bool) -> impl View<Rgb888, ()> {
    if_view!((is_square) {
        Rectangle
          .frame_sized(20, 20)
          .padding(Edges::All, 5)
          .transition(Slide::leading()) // Outermost modifier
    })
    .animated(Animation::ease_out(Duration::from_millis(120)), is_square)
}

fn all_the_same_2(is_square: bool) -> impl View<Rgb888, ()> {
    if_view!((is_square) {
        Rectangle
          .transition(Slide::leading()) // Innermost modifier
          .frame_sized(20, 20)
          .padding(Edges::All, 5)
    })
    .animated(Animation::ease_out(Duration::from_millis(120)), is_square)
}

}

Where To Apply Transitions

For views like VStack where there is no obvious way to choose between the transitions requested by its children, the default Opacity will be used.

The transitions applied inside the stack here have no effect:

#![allow(unused)]
fn main() {
extern crate buoyant;
extern crate embedded_graphics;
use embedded_graphics::pixelcolor::Rgb888;
use std::time::Duration;
use buoyant::{
    if_view,
    view::prelude::*,
    transition::{Move, Slide},
};

fn lost_in_the_stacks(thing: bool) -> impl View<Rgb888, ()> {
    if_view!((thing) {
        VStack::new((
            Rectangle.transition(Slide::trailing()),
            Rectangle.transition(Move::leading())
      )) // .transition(...) <-- Here would work!
    })
    .animated(Animation::ease_out(Duration::from_millis(120)), thing)
}
}

To get the stack in this example to transition, apply the transition outside the stack.

Prefer Computed Properties

Unless transitioning is the desired behavior, prefer using computed properties over conditional views when the conditional view’s branches are the same type.

#![allow(unused)]
fn main() {
extern crate buoyant;
extern crate embedded_graphics;
use buoyant::{
    if_view,
    view::prelude::*,
};
use embedded_graphics::pixelcolor::Rgb888;

/// This will jump between two different rectangles
fn bar1(is_wide: bool) -> impl View<Rgb888, ()> {
    if_view!((is_wide) {
        Rectangle.frame_sized(100, 5)
    } else {
        Rectangle.frame_sized(20, 5)
    })
}

/// This will animate the frame of the Rectangle
fn bar2(is_wide: bool) -> impl View<Rgb888, ()> {
    Rectangle.frame_sized(if is_wide { 100 } else { 20 }, 5)
}
}