1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
//! Animation time signals.
//!
//! Animations are built using signals that tick each animation frame. The
//! signal value is the number of milliseconds since the start of the animation,
//! so you just need to render the animation frame corresponding to the elapsed
//! time.
//!
//! See [`finite_animation`] and [`infinite_animation`] for examples.
use futures_signals::signal::{Signal, SignalExt};
use crate::task::{animation_timestamp, request_animation_frame};
/// Provide a finite time signal for animations.
///
/// The signal will tick each frame until `duration_millis` has elapsed. The
/// value will never exceed `duration_millis` and the last value will be
/// `None`, unless the signal is dropped first.
///
/// # Example
///
/// Slowly filling a progress bar:
///
/// ```no_run
/// # use html::{progress, Progress};
/// # use silkenweb::{animation::finite_animation, prelude::*};
/// const DURATION: f64 = 3000.0;
/// # let p: Progress =
/// progress().max(DURATION as f32).value(Sig(
/// finite_animation(DURATION).map(|time| time.unwrap_or(DURATION) as f32)
/// ));
/// ```
///
/// See [module-level documentation](self) for more details.
pub fn finite_animation(duration_millis: f64) -> impl Signal<Item = Option<f64>> + 'static {
animation_timestamp()
.map(move |time| {
if time < duration_millis {
request_animation_frame();
Some(time)
} else {
None
}
})
.dedupe()
}
/// Provide an infinite time signal for animations.
///
/// The signal will tick each frame until it is dropped.
///
/// # Example
///
/// A rotating square:
///
/// ```no_run
/// # use silkenweb::{animation::infinite_animation, prelude::*};
/// # use svg::{attributes::Presentation, content_type::Length::Px, rect, svg, Svg};
/// # let doc: Svg =
/// svg().width(200.0).height(200.0).child(
/// rect()
/// .x(Px(25.0))
/// .y(Px(25.0))
/// .width(Px(50.0))
/// .height(Px(50.0))
/// .transform(Sig(
/// infinite_animation().map(|time| format!("rotate({} 50 50)", time / 10.0))
/// )),
/// );
/// ```
///
/// See [module-level documentation](self) for more details.
pub fn infinite_animation() -> impl Signal<Item = f64> + 'static {
animation_timestamp()
.map(|time| {
request_animation_frame();
time
})
.dedupe()
}