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
10#[must_use = "a tween does nothing unless awaited"]
17pub struct Tween<T> {
18 signal: Signal<T>,
19 from: Option<T>,
20 target: T,
21 duration: f64,
22 ease: Rc<dyn Ease>,
23 start: Option<f64>,
24}
25
26impl<T: Lerp + Clone + 'static> Tween<T> {
27 pub(crate) fn new(signal: Signal<T>, target: T, duration: f64) -> Self {
28 Self {
29 signal,
30 from: None,
31 target,
32 duration: duration.max(0.0),
33 ease: Rc::new(presets::DEFAULT),
34 start: None,
35 }
36 }
37
38 pub fn ease(mut self, ease: impl Ease + 'static) -> Self {
40 self.ease = Rc::new(ease);
41 self
42 }
43
44 pub fn from(mut self, value: T) -> Self {
46 self.from = Some(value);
47 self
48 }
49}
50
51pub(crate) fn progress(start: f64, duration: f64) -> Option<f64> {
54 runtime::with(|rt| {
55 let end = start + duration;
56 let now = rt.now();
57 if now + TIME_EPSILON >= end {
58 rt.task_time.set(end);
59 None
60 } else {
61 Some(((now - start) / duration).clamp(0.0, 1.0))
62 }
63 })
64}
65
66impl<T: Lerp + Clone + Unpin + 'static> Future for Tween<T> {
67 type Output = ();
68
69 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
70 let this = self.get_mut();
71 let start = match this.start {
72 Some(s) => s,
73 None => {
74 this.from.get_or_insert_with(|| this.signal.peek());
75 *this.start.insert(runtime::time())
76 }
77 };
78 match progress(start, this.duration) {
79 None => this.signal.set(this.target.clone()),
80 Some(t) => {
81 let from = this.from.as_ref().expect("captured on first poll");
82 this.signal.set(from.lerp(&this.target, this.ease.ease(t)));
83 return Poll::Pending;
84 }
85 }
86 Poll::Ready(())
87 }
88}