oxygengine_backend_web/
app.rs

1use core::{
2    app::{App, AppParams, AppTimer, BackendAppRunner},
3    Scalar,
4};
5use std::{cell::RefCell, rc::Rc, time::Duration};
6use url::Url;
7use wasm_bindgen::{prelude::*, JsCast};
8
9fn window() -> web_sys::Window {
10    web_sys::window().expect("no global `window` exists")
11}
12
13fn request_animation_frame(f: &Closure<dyn FnMut()>) {
14    window()
15        .request_animation_frame(f.as_ref().unchecked_ref())
16        .expect("Could not perform `requestAnimationFrame`");
17}
18
19fn performance() -> web_sys::Performance {
20    window()
21        .performance()
22        .expect("`window.performance` does not exists")
23}
24
25pub fn web_app_params() -> AppParams {
26    let url = window()
27        .document()
28        .expect("`window.document` does not exists")
29        .location()
30        .expect("`window.document.location` does not exists")
31        .href()
32        .expect("`window.document.location.href` does not exists");
33    AppParams::new(
34        Url::parse(&url)
35            .expect("Could not parse application URL")
36            .query_pairs()
37            .into_iter()
38            .map(|(k, v)| (k.to_string(), v.to_string()))
39            .collect(),
40    )
41}
42
43pub struct WebAppTimer {
44    timer: Scalar,
45    last_timer: Scalar,
46    time: Duration,
47    time_seconds: Scalar,
48    delta_time: Duration,
49    delta_time_seconds: Scalar,
50    ticks: usize,
51}
52
53impl Default for WebAppTimer {
54    fn default() -> Self {
55        let current_time = performance().now() as Scalar * 0.001;
56        Self {
57            timer: current_time,
58            last_timer: current_time,
59            time: Duration::default(),
60            time_seconds: 0.0,
61            delta_time: Duration::default(),
62            delta_time_seconds: 0.0,
63            ticks: 0,
64        }
65    }
66}
67
68impl AppTimer for WebAppTimer {
69    fn tick(&mut self) {
70        let current_time = performance().now() as Scalar * 0.001;
71        self.delta_time_seconds = current_time - self.last_timer;
72        self.delta_time = Duration::new(
73            self.delta_time_seconds as u64,
74            (self.delta_time_seconds.fract() * 1e9) as u32,
75        );
76        self.time_seconds = current_time - self.timer;
77        self.time = Duration::new(
78            self.time_seconds as u64,
79            (self.time_seconds.fract() * 1e9) as u32,
80        );
81        self.last_timer = current_time;
82        self.ticks = self.ticks.wrapping_add(1);
83    }
84
85    fn time(&self) -> Duration {
86        self.time
87    }
88
89    fn time_seconds(&self) -> Scalar {
90        self.time_seconds
91    }
92
93    fn delta_time(&self) -> Duration {
94        self.delta_time
95    }
96
97    fn delta_time_seconds(&self) -> Scalar {
98        self.delta_time_seconds
99    }
100
101    fn ticks(&self) -> usize {
102        self.ticks
103    }
104}
105
106#[derive(Default)]
107pub struct WebAppRunner;
108
109impl BackendAppRunner<JsValue> for WebAppRunner {
110    fn run(&mut self, app: Rc<RefCell<App>>) -> Result<(), JsValue> {
111        let f = Rc::new(RefCell::new(None));
112        let g = f.clone();
113        *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
114            if !app.borrow().multiverse.is_running() {
115                drop(f.borrow_mut().take());
116                return;
117            }
118            app.borrow_mut().process();
119            request_animation_frame(f.borrow().as_ref().unwrap());
120        }) as Box<dyn FnMut()>));
121        request_animation_frame(g.borrow().as_ref().unwrap());
122        Ok(())
123    }
124}