forked from rustwasm/gloo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
254 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[package] | ||
name = "gloo-ani-frames" | ||
version = "0.1.0" | ||
authors = ["Rust and WebAssembly Working Group"] | ||
edition = "2018" | ||
|
||
[dependencies] | ||
wasm-bindgen = "0.2.40" | ||
js-sys = "0.3.17" | ||
|
||
[dependencies.web-sys] | ||
version = "0.3.17" | ||
features = [ | ||
"console", | ||
"Window", | ||
] | ||
|
||
[dev-dependencies] | ||
wasm-bindgen-test = "0.2.40" | ||
futures = "0.1.25" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
use std::marker::PhantomData; | ||
use std::mem::{replace}; | ||
use std::rc::Rc; | ||
use std::sync::Mutex; | ||
|
||
use wasm_bindgen::{JsValue, JsCast, UnwrapThrowExt, closure::Closure}; | ||
use std::fmt::Debug; | ||
|
||
#[inline] | ||
fn request_af<F: FnOnce() + 'static>(cls: F) -> i32 { | ||
web_sys::window().unwrap_throw() | ||
.request_animation_frame(Closure::once(cls).as_ref().unchecked_ref()) | ||
.unwrap_throw() | ||
} | ||
|
||
#[inline] | ||
fn cancel_af(index: i32) -> Result<(), JsValue> { | ||
web_sys::window().unwrap_throw() | ||
.cancel_animation_frame(index) | ||
} | ||
|
||
|
||
pub struct Animation<C: 'static>(Mutex<Ani<C>>); | ||
|
||
impl Animation<()> { | ||
pub fn request_frame<F: FnOnce() + 'static>(callback: F) -> AniIndex<F> { | ||
AniIndex::new(request_af(callback)) | ||
} | ||
|
||
pub fn cancel_frame<F>(index: AniIndex<F>) { | ||
cancel_af(index.inner).unwrap_throw() | ||
} | ||
} | ||
|
||
impl<C: 'static> Animation<C> { | ||
fn from_ani(ani: Ani<C>) -> Self { | ||
Animation(Mutex::new(ani)) | ||
} | ||
|
||
pub fn new() -> Rc<Self> { | ||
let ani = Rc::new(Animation::from_ani(Ani::paused())); | ||
ani.start(); | ||
ani | ||
} | ||
|
||
pub fn paused() -> Rc<Self> { | ||
Rc::new(Animation::from_ani(Ani::paused())) | ||
} | ||
|
||
pub fn add<F>(&mut self, cb: &'static F) -> AniIndex<C> | ||
where F: Fn(&AniState) + 'static | ||
{ | ||
let mut guard = self.0.lock().unwrap_throw(); | ||
|
||
let new_ix = AniIndex::new(guard.next_index.inner + 1); | ||
let old_ix = replace(&mut guard.next_index, new_ix); | ||
|
||
guard.callbacks.push((AniIndex::new(old_ix.inner), cb)); | ||
return old_ix | ||
} | ||
|
||
pub fn remove(&mut self, index: AniIndex<C>) -> Result<(), ()> { | ||
let mut guard = self.0.lock().unwrap_throw(); | ||
|
||
let pos = guard.callbacks.iter() | ||
.position(|(ix, _)| ix.inner == index.inner) | ||
.ok_or(())?; | ||
guard.callbacks.remove(pos); | ||
Ok(()) | ||
} | ||
|
||
|
||
fn do_loop(ani: Rc<Self>) { | ||
let cloned = ani.clone(); | ||
let num = request_af(|| Self::do_loop(cloned)); | ||
|
||
let mut guard = ani.0.lock().unwrap_throw(); | ||
guard.state = AniState::Running(num); | ||
|
||
for (_, cb) in &guard.callbacks { | ||
cb(&guard.state) | ||
} | ||
} | ||
} | ||
|
||
pub trait AnimationRc { | ||
fn start(&self); | ||
fn pause(&self); | ||
fn once(&self); | ||
} | ||
|
||
impl<C> AnimationRc for Rc<Animation<C>> { | ||
fn start(&self) { | ||
let mut guard = self.0.lock().unwrap_throw(); | ||
match guard.state { | ||
AniState::Paused => { | ||
let cloned = self.clone(); | ||
let num = request_af(move || Animation::do_loop(cloned)); | ||
guard.state = AniState::Running(num); | ||
} | ||
AniState::Running(_) => (), | ||
AniState::RunningOnce(ix) => { | ||
cancel_af(ix).unwrap_throw(); | ||
|
||
let cloned = self.clone(); | ||
let num = request_af(|| Animation::do_loop(cloned)); | ||
guard.state = AniState::Running(num); | ||
} | ||
} | ||
} | ||
|
||
fn pause(&self) { | ||
let mut guard = self.0.lock().unwrap_throw(); | ||
match guard.state { | ||
AniState::Paused => (), | ||
AniState::Running(ix) => { | ||
cancel_af(ix).unwrap_throw(); | ||
guard.state = AniState::Paused; | ||
} | ||
AniState::RunningOnce(ix) => { | ||
cancel_af(ix).unwrap_throw(); | ||
guard.state = AniState::Paused; | ||
} | ||
} | ||
} | ||
|
||
fn once(&self) { | ||
let mut guard = self.0.lock().unwrap_throw(); | ||
match guard.state { | ||
AniState::Paused => { | ||
let cloned = self.clone(); | ||
let num = request_af(move || { | ||
let guard = cloned.0.lock().unwrap_throw(); | ||
|
||
for (_, cb) in &guard.callbacks { | ||
cb(&guard.state) | ||
} | ||
}); | ||
guard.state = AniState::RunningOnce(num); | ||
} | ||
AniState::Running(_) | AniState::RunningOnce(_) => () | ||
} | ||
} | ||
} | ||
|
||
|
||
struct Ani<C: 'static> { | ||
state: AniState, | ||
next_index: AniIndex<C>, | ||
callbacks: Vec<(AniIndex<C>, &'static dyn Fn(&AniState))>, | ||
} | ||
|
||
impl<C: 'static> Ani<C> { | ||
fn paused() -> Self { | ||
Ani { | ||
state: AniState::Paused, | ||
next_index: AniIndex::new(1), | ||
callbacks: vec![] | ||
} | ||
} | ||
} | ||
|
||
#[derive(Copy, Clone, Debug)] | ||
pub enum AniState { | ||
Paused, | ||
Running(i32), | ||
RunningOnce(i32), | ||
} | ||
|
||
pub struct AniIndex<C> { | ||
inner: i32, | ||
_marker: PhantomData<C>, | ||
} | ||
|
||
impl<C> Debug for AniIndex<C> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { | ||
write!(f, "AniIndex({})", self.inner) | ||
} | ||
} | ||
|
||
impl<C> AniIndex<C> { | ||
fn new(index: i32) -> Self { | ||
AniIndex { inner: index, _marker: PhantomData } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
//! | ||
//! | ||
|
||
mod callbacks; | ||
pub use callbacks::{Animation as Animation, AniIndex, AniState}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
#![cfg(target_arch = "wasm32")] | ||
|
||
use gloo_ani_frames::*; | ||
use futures::prelude::*; | ||
|
||
use wasm_bindgen_test::*; | ||
use std::future::Future; | ||
|
||
wasm_bindgen_test_configure!(run_in_browser); | ||
|
||
#[wasm_bindgen_test(async)] | ||
pub fn test() -> impl Future<> { | ||
let mut is_executed = false; | ||
let index1 = Animation::request_frame(|| { | ||
is_executed = true; | ||
}); | ||
let index2 = Animation::request_frame(|| { | ||
unreachable!() | ||
}); | ||
Animation::cancel_frame(index2); | ||
|
||
Animation::request_frame(|| { | ||
Animation::request_frame(|| { | ||
assert_eq!(is_executed, true); | ||
|
||
let mut ani = Animation::<()>::new(); | ||
|
||
let mut is_executed = false; | ||
let ix1 = ani.add(|s| { | ||
unreachable!(); | ||
}); | ||
let ix2 = ani.add(|s| { | ||
is_executed = true | ||
}); | ||
ani.remove(ix1); | ||
ani.add(|s| { | ||
assert_eq!(); | ||
}); | ||
}); | ||
}); | ||
; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters