aura-anim-iced 0.2.2

Iced integration for the aura typed animation runtime.
Documentation
//! Visual Keyframes example with per-segment easing.

use std::time::Instant;

use aura_anim_core::{
    Animatable, Motion, MotionRuntime,
    keyframes::Keyframes,
    timing::{Easing, IterationCount},
};
use iced::{
    Background, Border, Color, Element, Fill, Shadow, Subscription, Theme, Vector,
    widget::{button, column, container, row, text},
};

#[derive(Clone, Debug, Animatable)]
struct Ball {
    x: f32,
    y: f32,
    scale: f32,
    hue: Color,
}

struct KeyframesExample {
    runtime: MotionRuntime,
    ball: Motion<Ball>,
}

#[derive(Clone, Copy, Debug)]
enum Message {
    Frame(Instant),
    Replay,
}

fn main() -> iced::Result {
    iced::application(
        KeyframesExample::new,
        KeyframesExample::update,
        KeyframesExample::view,
    )
    .title("Aura Anim - Keyframes")
    .theme(theme)
    .subscription(KeyframesExample::subscription)
    .window_size((820.0, 520.0))
    .run()
}

impl KeyframesExample {
    fn new() -> Self {
        let mut runtime = MotionRuntime::new();
        let ball = runtime.motion(start());
        let mut example = Self { runtime, ball };
        example.replay();
        example
    }

    fn replay(&mut self) {
        self.ball.play(animation(), &mut self.runtime);
    }

    fn update(&mut self, message: Message) {
        match message {
            Message::Frame(now) => aura_anim_iced::frame(&mut self.runtime, now),
            Message::Replay => self.replay(),
        }
    }

    fn subscription(&self) -> Subscription<Message> {
        aura_anim_iced::subscription(&self.runtime).map(Message::Frame)
    }

    fn view(&self) -> Element<'_, Message> {
        let ball = self.ball.value_ref(&self.runtime);
        let size = 46.0 * ball.scale;
        let hue = ball.hue;
        let marker = container("")
            .width(size)
            .height(size)
            .style(move |_| container::Style {
                background: Some(Background::Color(hue)),
                border: Border::default().rounded(size / 2.0),
                shadow: Shadow {
                    color: Color::from_rgba(hue.r, hue.g, hue.b, 0.55),
                    offset: Vector::ZERO,
                    blur_radius: 24.0,
                },
                ..container::Style::default()
            });

        let stage = container(
            column![
                container("").height(ball.y),
                row![
                    container("").width(ball.x),
                    marker,
                    container("").width(Fill)
                ]
            ]
            .width(Fill),
        )
        .width(Fill)
        .height(250)
        .style(|_| stage_style());

        container(
            column![
                text("Keyframes").size(34).color(Color::WHITE),
                text("Four time-positioned poses create anticipation, overshoot, and recovery.")
                    .size(14)
                    .color(Color::from_rgb8(174, 182, 211)),
                stage,
                row![
                    button("Replay").on_press(Message::Replay),
                    text("0 ms").size(12),
                    text("350 ms: anticipate").size(12),
                    text("760 ms: overshoot").size(12),
                    text("1200 ms: settle").size(12)
                ]
                .spacing(16),
                text(format!(
                    "{:?}  position: ({:.0}, {:.0})  scale: {:.2}",
                    self.ball.state(&self.runtime),
                    ball.x,
                    ball.y,
                    ball.scale
                ))
                .size(13)
                .color(Color::from_rgb8(255, 189, 116))
            ]
            .spacing(18),
        )
        .width(Fill)
        .height(Fill)
        .padding(34)
        .style(|_| page_style())
        .into()
    }
}

fn start() -> Ball {
    Ball {
        x: 30.0,
        y: 140.0,
        scale: 1.0,
        hue: Color::from_rgb8(93, 214, 190),
    }
}

fn animation() -> Keyframes<Ball> {
    Keyframes::new(start())
        .push_eased(
            350.0,
            Ball {
                x: 8.0,
                y: 154.0,
                scale: 0.82,
                hue: Color::from_rgb8(93, 214, 190),
            },
            Easing::EaseIn,
        )
        .push_eased(
            760.0,
            Ball {
                x: 650.0,
                y: 30.0,
                scale: 1.35,
                hue: Color::from_rgb8(255, 118, 157),
            },
            Easing::EaseOut,
        )
        .push_eased(
            1200.0,
            Ball {
                x: 570.0,
                y: 116.0,
                scale: 1.0,
                hue: Color::from_rgb8(119, 105, 255),
            },
            Easing::EaseInOut,
        )
        .with_iterations(IterationCount::ONCE)
}

fn theme(_: &KeyframesExample) -> Theme {
    Theme::TokyoNight
}

fn page_style() -> container::Style {
    container::Style::default()
        .background(Color::from_rgb8(18, 21, 34))
        .color(Color::from_rgb8(174, 182, 211))
}

fn stage_style() -> container::Style {
    container::Style {
        background: Some(Background::Color(Color::from_rgb8(27, 32, 50))),
        border: Border::default()
            .rounded(8)
            .width(1)
            .color(Color::from_rgb8(61, 70, 99)),
        ..container::Style::default()
    }
}