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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#![deny(missing_docs)]
use futures::task::{waker, ArcWake};
use std::{
cell::{Cell, RefCell},
rc::Rc,
sync::Arc,
task::Waker,
};
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::window;
pub trait Tick: 'static {
fn tick(&mut self);
}
pub trait Waking {
fn set_waker(&mut self, wk: Waker);
}
#[must_use]
pub struct AnimationFrameScheduler<Cb>(Rc<AnimationFrameState<Cb>>);
struct AnimationFrameState<Cb> {
ticker: RefCell<Cb>,
handle: Cell<Option<AnimationFrameHandle>>,
}
impl<T: Tick> ArcWake for AnimationFrameScheduler<T> {
fn wake_by_ref(arc_self: &Arc<AnimationFrameScheduler<T>>) {
arc_self.ensure_scheduled(false);
}
}
impl<T: Tick> AnimationFrameScheduler<T> {
pub fn new(ticker: T) -> Self {
AnimationFrameScheduler(Rc::new(AnimationFrameState {
ticker: RefCell::new(ticker),
handle: Cell::new(None),
}))
}
fn ensure_scheduled(&self, immediately_again: bool) {
let existing = self.0.handle.replace(None);
let handle = existing.unwrap_or_else(|| {
let self2 = AnimationFrameScheduler(Rc::clone(&self.0));
let callback = Closure::once(Box::new(move || {
self2.0.handle.set(None);
self2.0.ticker.borrow_mut().tick();
if immediately_again {
self2.ensure_scheduled(true);
}
}));
AnimationFrameHandle::request(callback)
});
self.0.handle.set(Some(handle));
}
pub fn run_on_every_frame(self) {
self.ensure_scheduled(true);
}
}
impl<T: Tick + Waking> AnimationFrameScheduler<T> {
pub fn run_on_wake(self) {
let state = Rc::clone(&self.0);
let waker = waker(Arc::new(self));
state.ticker.borrow_mut().set_waker(waker);
state.ticker.borrow_mut().tick();
}
}
unsafe impl<Cb> Send for AnimationFrameScheduler<Cb> {}
unsafe impl<Cb> Sync for AnimationFrameScheduler<Cb> {}
struct AnimationFrameHandle {
raw: i32,
_callback: Closure<dyn FnMut()>,
}
impl AnimationFrameHandle {
fn request(callback: Closure<dyn FnMut()>) -> Self {
let raw =
window().unwrap().request_animation_frame(callback.as_ref().unchecked_ref()).unwrap();
Self { raw, _callback: callback }
}
}
impl Drop for AnimationFrameHandle {
fn drop(&mut self) {
window().unwrap().cancel_animation_frame(self.raw).ok();
}
}