dioxus_spring/
use_spring_ref.rs

1use crate::spring;
2use dioxus::prelude::*;
3use futures::StreamExt;
4use interpolation::Lerp;
5use std::{collections::VecDeque, pin::Pin, task::Poll, time::Duration};
6
7/// Hook to animate a value from some initial value `from`.
8///
9/// The returned [`UseSpringRef`] can be used to queue and control animations.
10/// Values are linearly interpolated and sent to the handler `f`.
11pub fn use_spring_ref<V>(from: V, f: impl FnMut(V) + 'static) -> UseSpringRef<V>
12where
13    V: Lerp<Scalar = f32> + Clone + 'static,
14{
15    let mut channel = use_hook(|| {
16        let (tx, rx) = async_channel::unbounded();
17        CopyValue::new((tx, Box::pin(rx)))
18    });
19    let mut f_cell = Some(f);
20
21    use_future(move || {
22        let mut f = f_cell.take().unwrap();
23        let mut current = from.clone();
24        let mut spring_cell = None;
25        let mut stack = VecDeque::new();
26
27        futures::future::poll_fn(move |cx| {
28            while let Poll::Ready(Some(msg)) = channel.write().1.poll_next_unpin(cx) {
29                match msg {
30                    Message::Set(to, duration_cell) => {
31                        if let Some(duration) = duration_cell {
32                            let spring = spring(current.clone(), to, duration);
33                            spring_cell = Some(Box::pin(spring));
34                            stack.clear();
35                        } else {
36                            current = to.clone();
37                            spring_cell.take();
38                            stack.clear();
39                            f(to);
40                        }
41                    }
42                    Message::Queue(to, duration) => {
43                        stack.push_back((to, duration));
44                    }
45                }
46            }
47
48            if spring_cell.is_none() {
49                if let Some((to, duration)) = stack.pop_front() {
50                    let spring = crate::spring(current.clone(), to, duration);
51                    spring_cell = Some(Box::pin(spring));
52                } else {
53                    spring_cell = None;
54                }
55            }
56
57            while let Some(spring) = spring_cell.as_mut() {
58                let mut is_done = false;
59                while let Poll::Ready(item) = spring.poll_next_unpin(cx) {
60                    if let Some(val) = item {
61                        current = val.clone();
62                        f(val);
63                    } else {
64                        is_done = true;
65                        break;
66                    }
67                }
68
69                if is_done {
70                    if let Some((to, duration)) = stack.pop_front() {
71                        let spring = crate::spring(current.clone(), to, duration);
72                        spring_cell = Some(Box::pin(spring));
73                    } else {
74                        spring_cell = None;
75                    }
76                } else {
77                    break;
78                }
79            }
80
81            Poll::<()>::Pending
82        })
83    });
84
85    UseSpringRef { channel }
86}
87
88pub(crate) enum Message<V> {
89    Set(V, Option<Duration>),
90    Queue(V, Duration),
91}
92
93/// Hook returned from [`use_spring_ref`]
94pub struct UseSpringRef<V: 'static> {
95    channel: CopyValue<(
96        async_channel::Sender<Message<V>>,
97        Pin<Box<async_channel::Receiver<Message<V>>>>,
98    )>,
99}
100
101impl<V> UseSpringRef<V> {
102    pub fn set(&self, to: V) {
103        self.channel
104            .read()
105            .0
106            .force_send(Message::Set(to, None))
107            .unwrap();
108    }
109
110    pub fn animate(&self, to: V, duration: Duration) {
111        self.channel
112            .read()
113            .0
114            .force_send(Message::Set(to, Some(duration)))
115            .unwrap();
116    }
117
118    pub fn queue(&self, to: V, duration: Duration) {
119        self.channel
120            .read()
121            .0
122            .force_send(Message::Queue(to, duration))
123            .unwrap();
124    }
125}
126
127impl<V> Clone for UseSpringRef<V> {
128    fn clone(&self) -> Self {
129        *self
130    }
131}
132
133impl<V> Copy for UseSpringRef<V> {}