oxygengine_backend_web/
app.rs1use 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}