#![allow(clippy::type_complexity)]
use wasm_bindgen::prelude::*;
use crate::driver::AnimationDriver;
#[wasm_bindgen]
pub struct RafDriver {
inner: AnimationDriver,
last_timestamp: Option<f64>,
paused: bool,
time_scale: f32,
}
#[wasm_bindgen]
impl RafDriver {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
inner: AnimationDriver::new(),
last_timestamp: None,
paused: false,
time_scale: 1.0,
}
}
pub fn tick(&mut self, timestamp_ms: f64) {
if self.paused {
self.last_timestamp = Some(timestamp_ms);
return;
}
let dt = match self.last_timestamp {
Some(last) => ((timestamp_ms - last) / 1000.0) as f32, None => 0.0,
};
self.last_timestamp = Some(timestamp_ms);
let dt = dt.clamp(0.0, 0.5) * self.time_scale;
self.inner.tick(dt);
}
pub fn active_count(&self) -> usize {
self.inner.active_count()
}
pub fn reset_timestamp(&mut self) {
self.last_timestamp = None;
}
pub fn pause(&mut self) {
self.paused = true;
}
pub fn resume(&mut self) {
self.paused = false;
}
pub fn is_paused(&self) -> bool {
self.paused
}
pub fn set_time_scale(&mut self, scale: f32) {
self.time_scale = scale.max(0.0);
}
pub fn get_time_scale(&self) -> f32 {
self.time_scale
}
pub fn on_visibility_change(&mut self, hidden: bool) {
if hidden {
self.last_timestamp = None;
}
}
}
impl RafDriver {
pub fn add<A: crate::traits::Update + 'static>(
&mut self,
animation: A,
) -> crate::driver::AnimationId {
self.inner.add(animation)
}
pub fn cancel(&mut self, id: crate::driver::AnimationId) {
self.inner.cancel(id);
}
pub fn cancel_all(&mut self) {
self.inner.cancel_all();
}
pub fn driver(&self) -> &AnimationDriver {
&self.inner
}
pub fn driver_mut(&mut self) -> &mut AnimationDriver {
&mut self.inner
}
}
impl Default for RafDriver {
fn default() -> Self {
Self::new()
}
}
impl core::fmt::Debug for RafDriver {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RafDriver")
.field("active_count", &self.inner.active_count())
.field("last_timestamp", &self.last_timestamp)
.field("paused", &self.paused)
.field("time_scale", &self.time_scale)
.finish()
}
}
pub fn start_raf_loop(mut callback: impl FnMut(f64) + 'static) {
use std::cell::RefCell;
use std::rc::Rc;
let f: Rc<RefCell<Option<Closure<dyn FnMut(f64)>>>> = Rc::new(RefCell::new(None));
let g = f.clone();
*g.borrow_mut() = Some(Closure::wrap(Box::new(move |timestamp: f64| {
callback(timestamp);
if let Some(ref closure) = *f.borrow() {
let _ = web_sys_request_animation_frame(closure);
}
}) as Box<dyn FnMut(f64)>));
{
let guard = g.borrow();
if let Some(ref closure) = *guard {
let _ = web_sys_request_animation_frame(closure);
}
}
}
fn web_sys_request_animation_frame(closure: &Closure<dyn FnMut(f64)>) -> Result<i32, JsValue> {
js_sys::Reflect::get(&js_sys::global(), &"requestAnimationFrame".into())
.and_then(|raf| {
let raf = js_sys::Function::from(raf);
raf.call1(&JsValue::NULL, closure.as_ref().unchecked_ref())
})
.map(|v| v.as_f64().unwrap_or(0.0) as i32)
}