1use std::future::Future;
2use std::pin::Pin;
3use std::rc::Rc;
4use std::task::{Context, Poll};
5
6use crate::ease::presets;
7use crate::runtime;
8use crate::{Ease, Lerp, Signal, TIME_EPSILON};
9
10pub trait Seconds {
12 fn seconds(self) -> f64;
14}
15
16impl Seconds for f64 {
17 fn seconds(self) -> f64 {
18 self
19 }
20}
21
22#[must_use = "a tween does nothing unless awaited"]
29pub struct Tween<T> {
30 signal: Signal<T>,
31 from: Option<T>,
32 target: T,
33 duration: f64,
34 ease: Rc<dyn Ease>,
35 start: Option<f64>,
36}
37
38impl<T: Lerp + Clone + 'static> Tween<T> {
39 pub(crate) fn new(signal: Signal<T>, target: T, duration: f64) -> Self {
40 Self {
41 signal,
42 from: None,
43 target,
44 duration: duration.max(0.0),
45 ease: Rc::new(presets::DEFAULT),
46 start: None,
47 }
48 }
49
50 pub fn ease(mut self, ease: impl Ease + 'static) -> Self {
52 self.ease = Rc::new(ease);
53 self
54 }
55
56 pub fn from(mut self, value: T) -> Self {
58 self.from = Some(value);
59 self
60 }
61}
62
63pub(crate) fn progress(start: f64, duration: f64) -> Option<f64> {
66 runtime::with(|rt| {
67 let end = start + duration;
68 let now = rt.now();
69 if now + TIME_EPSILON >= end {
70 rt.task_time.set(end);
71 None
72 } else {
73 Some(((now - start) / duration).clamp(0.0, 1.0))
74 }
75 })
76}
77
78impl<T: Lerp + Clone + Unpin + 'static> Future for Tween<T> {
79 type Output = ();
80
81 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
82 let this = self.get_mut();
83 let start = match this.start {
84 Some(s) => s,
85 None => {
86 this.from.get_or_insert_with(|| this.signal.peek());
87 *this.start.insert(runtime::time())
88 }
89 };
90 match progress(start, this.duration) {
91 None => this.signal.set(this.target.clone()),
92 Some(t) => {
93 let from = this.from.as_ref().expect("captured on first poll");
94 this.signal.set(from.lerp(&this.target, this.ease.ease(t)));
95 return Poll::Pending;
96 }
97 }
98 Poll::Ready(())
99 }
100}