// anim // // A framework independent animation library for rust, works nicely with Iced and the others // Copyright: 2021, Joylei // License: MIT use anim::{easing, timeline::Status, Options, Timeline}; use iced::{ button, Align, Application, Button, Clipboard, Column, Command, Container, HorizontalAlignment, Length, Row, Size, Subscription, Text, VerticalAlignment, }; use std::time::Duration; fn main() { State::run(Default::default()).unwrap(); } #[derive(Debug, Clone)] enum Message { Idle, /// animation frame Tick, Start, Pause, Stop, } struct State { btn_start: button::State, btn_pause: button::State, btn_stop: button::State, btn_test: button::State, timeline: Timeline, } impl Application for State { type Executor = iced::executor::Default; type Flags = (); type Message = self::Message; fn new(_flags: ()) -> (Self, Command) { let mut timeline: Timeline<_> = Options::new(Size::new(130.0, 30.0), Size::new(500.0, 200.0)) .duration(Duration::from_secs(2)) .auto_reverse(true) .easing(easing::bounce_ease()) .forever() .into(); timeline.begin(); let app = Self { btn_start: Default::default(), btn_pause: Default::default(), btn_stop: Default::default(), btn_test: Default::default(), timeline, }; (app, Command::none()) } fn title(&self) -> String { "Size animation example".to_owned() } fn update(&mut self, message: Self::Message, _clipboard: &mut Clipboard) -> Command { match message { Message::Tick => { self.timeline.update(); } Message::Start => { let status = self.timeline.status(); if status == Status::Paused { self.timeline.resume(); } else { self.timeline.begin(); } } Message::Pause => { self.timeline.pause(); } Message::Stop => { self.timeline.stop(); } _ => {} } Command::none() } fn view(&mut self) -> iced::Element<'_, Self::Message> { let status = self.timeline.status(); let size = self.timeline.value(); //eprintln!("{:?}", status); let controls = Row::new() .spacing(10) .push(button_optional( &mut self.btn_start, "Start", if status != Status::Animating { Message::Start.into() } else { None }, )) .push(button_optional( &mut self.btn_pause, "Pause", if status == Status::Animating { Message::Pause.into() } else { None }, )) .push(button_optional( &mut self.btn_stop, "Stop", if status == Status::Animating || status == Status::Paused { Message::Stop.into() } else { None }, )); let content = Column::new() .spacing(20) .align_items(Align::Start) .width(Length::Fill) .height(Length::Fill) .push(controls) .push( Container::new( Button::new( &mut self.btn_test, Text::new("size changes") .horizontal_alignment(HorizontalAlignment::Center) .vertical_alignment(VerticalAlignment::Center) .width(Length::Fill) .height(Length::Fill), ) .style(style::Button) .width(Length::Units(size.width as u16)) .height(Length::Units(size.height as u16)) .on_press(Message::Idle), ) .align_x(Align::Center) .align_y(Align::Center) .width(Length::Fill) .height(Length::Fill), ); Container::new(content) .align_x(iced::Align::Center) .align_y(iced::Align::Start) .padding(10) .width(Length::Fill) .height(Length::Fill) .into() } fn subscription(&self) -> Subscription { let status = self.timeline.status(); if status.is_animating() { const FPS: f32 = 60.0; iced::time::every(Duration::from_secs_f32(1.0 / FPS)).map(|_tick| Message::Tick) } else { iced::Subscription::none() } } } fn button_optional<'a, Message: Clone>( state: &'a mut button::State, label: &str, on_press: Option, ) -> Button<'a, Message> { let mut btn = Button::new(state, Text::new(label)); if let Some(on_press) = on_press { btn = btn.on_press(on_press); } btn } mod style { use iced::{button, Color}; pub struct Button; impl button::StyleSheet for Button { fn active(&self) -> button::Style { button::Style { background: Some(Color::BLACK.into()), text_color: Color::WHITE, ..Default::default() } } } }