yew_stdweb/services/
render.rs

1//! This module contains Yew's implementation of a service which can be used to
2//! request frame rendering
3
4use crate::callback::Callback;
5use crate::services::Task;
6use cfg_if::cfg_if;
7use cfg_match::cfg_match;
8use std::fmt;
9cfg_if! {
10    if #[cfg(feature = "std_web")] {
11        #[allow(unused_imports)]
12        use stdweb::{_js_impl, js};
13        use stdweb::unstable::TryInto;
14        use stdweb::Value;
15    } else if #[cfg(feature = "web_sys")] {
16        use crate::utils;
17        use wasm_bindgen::closure::Closure;
18        use wasm_bindgen::{JsCast, JsValue};
19    }
20}
21
22/// A handle to cancel a render task.
23#[must_use = "the task will be cancelled when the task is dropped"]
24pub struct RenderTask(
25    #[cfg(feature = "std_web")] Value,
26    #[cfg(feature = "web_sys")] RenderTaskInner,
27);
28
29#[cfg(feature = "web_sys")]
30struct RenderTaskInner {
31    render_id: i32,
32    #[allow(dead_code)]
33    callback: Closure<dyn Fn(JsValue)>,
34}
35
36impl fmt::Debug for RenderTask {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        f.write_str("RenderTask")
39    }
40}
41
42/// A service to request animation frames.
43#[derive(Default, Debug)]
44pub struct RenderService {}
45
46impl RenderService {
47    /// Request animation frame. Callback will be notified when frame should be rendered.
48    pub fn request_animation_frame(callback: Callback<f64>) -> RenderTask {
49        let callback = move |#[cfg(feature = "std_web")] v,
50                             #[cfg(feature = "web_sys")] v: JsValue| {
51            let time: f64 = cfg_match! {
52                feature = "std_web" => ({
53                    match v {
54                        Value::Number(n) => n.try_into().unwrap(),
55                        _ => 0.0,
56                    }
57                }),
58                feature = "web_sys" => v.as_f64().unwrap_or(0.),
59            };
60            callback.emit(time);
61        };
62        let handle = cfg_match! {
63            feature = "std_web" => js! {
64                var callback = @{callback};
65                var action = function(time) {
66                    callback(time);
67                    callback.drop();
68                };
69                return {
70                    render_id: requestAnimationFrame(action),
71                    callback: callback,
72                };
73            },
74            feature = "web_sys" => ({
75                let callback = Closure::wrap(Box::new(callback) as Box<dyn Fn(JsValue)>);
76                let render_id = utils::window().request_animation_frame(callback.as_ref().unchecked_ref()).unwrap();
77                RenderTaskInner {
78                    render_id,
79                    callback,
80                }
81            }),
82        };
83        RenderTask(handle)
84    }
85}
86
87impl Task for RenderTask {
88    fn is_active(&self) -> bool {
89        true
90    }
91}
92
93impl Drop for RenderTask {
94    fn drop(&mut self) {
95        if self.is_active() {
96            cfg_match! {
97                feature = "std_web" => ({
98                    let handle = &self.0;
99                    js! { @(no_return)
100                        var handle = @{handle};
101                        cancelAnimationFrame(handle.render_id);
102                        handle.callback.drop();
103                    }
104                }),
105                feature = "web_sys" => utils::window().cancel_animation_frame(self.0.render_id).unwrap(),
106            }
107        }
108    }
109}