diff --git a/README.md b/README.md index 6c47c2f63..cfc3540f7 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,7 @@ It's a handy alternative to subscriptions. Implemented: * `IntervalService` +* `RenderService` * `TimeoutService` * `StorageService` * `DialogService` diff --git a/src/services/mod.rs b/src/services/mod.rs index b1044e2fd..082721663 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -7,6 +7,7 @@ pub mod console; pub mod dialog; pub mod fetch; pub mod interval; +pub mod render; pub mod storage; pub mod timeout; pub mod websocket; @@ -15,6 +16,7 @@ pub use self::console::ConsoleService; pub use self::dialog::DialogService; pub use self::fetch::FetchService; pub use self::interval::IntervalService; +pub use self::render::RenderService; pub use self::storage::StorageService; pub use self::timeout::TimeoutService; pub use self::websocket::WebSocketService; diff --git a/src/services/render.rs b/src/services/render.rs new file mode 100644 index 000000000..2dbeb74d3 --- /dev/null +++ b/src/services/render.rs @@ -0,0 +1,66 @@ +//! This module contains the implementation of a service to +//! request frame rendering + +use stdweb::Value; +use stdweb::unstable::TryInto; +use services::Task; +use callback::Callback; + +/// A handle to cancel a render task. +pub struct RenderTask(Option); + +/// A service to request animation frames. +#[derive(Default)] +pub struct RenderService {} + +impl RenderService { + /// Create a new service instance + pub fn new() -> Self { + Self {} + } + + /// Request animation frame. Callback will be notified when frame should be rendered. + pub fn request_animation_frame(&mut self, callback: Callback) -> RenderTask { + let callback = move |v| { + let time: f64 = match v { + Value::Number(n) => n.try_into().unwrap(), + _ => 0.0 + }; + callback.emit(time); + }; + let handle = js! { + var callback = @{callback}; + var action = function(time) { + callback(time); + callback.drop(); + }; + return { + render_id: requestAnimationFrame(action), + callback: callback, + }; + }; + RenderTask(Some(handle)) + } +} + +impl Task for RenderTask { + fn is_active(&self) -> bool { + self.0.is_some() + } + fn cancel(&mut self) { + let handle = self.0.take().expect("tried to cancel render twice"); + js! { @(no_return) + var handle = @{handle}; + cancelAnimationFrame(handle.timeout_id); + handle.callback.drop(); + } + } +} + +impl Drop for RenderTask { + fn drop(&mut self) { + if self.is_active() { + self.cancel(); + } + } +}