tawasuyu

tawasuyu/brahman

Rust 212TOML 64JSON 13HTML 12666 Definiciones

⎇ 52acaabcf42e81de31acf884f80c607fe8b829fe crates/apps/gioser-web/src/lib.rs Rust · 149 líneas · 5.0 KB Historia Quién tocó qué crudo

gioser 52acaabcf · sergio ·

1//! Entrypoint WASM de la landing GioSer.
2//!
3//! Monta el canvas, instala listeners de mouse y resize, corre el loop
4//! con `requestAnimationFrame`, y reposiciona los 4 botones DOM
5//! `#tip-{aire|fuego|tierra|agua}` sobre las puntas proyectadas
6//! de la chacana cada frame.
7
8use std::cell::RefCell;
9use std::rc::Rc;
10
11use gioser_canvas_web::{tips, Renderer};
12use wasm_bindgen::prelude::*;
13use wasm_bindgen::JsCast;
14use web_sys::{Document, HtmlCanvasElement, HtmlElement, MouseEvent, Window};
15
16#[wasm_bindgen(start)]
17pub fn boot() -> Result<(), JsValue> {
18    install_panic_hook();
19    let window = web_sys::window().ok_or_else(|| JsValue::from_str("no window"))?;
20    let document = window
21        .document()
22        .ok_or_else(|| JsValue::from_str("no document"))?;
23
24    let canvas: HtmlCanvasElement = document
25        .get_element_by_id("gioser-canvas")
26        .ok_or_else(|| JsValue::from_str("no canvas#gioser-canvas"))?
27        .dyn_into()?;
28
29    fit_canvas(&canvas, &window);
30    let renderer = Rc::new(RefCell::new(Renderer::new(&canvas)?));
31    renderer
32        .borrow_mut()
33        .resize(canvas.width(), canvas.height());
34
35    install_resize(&window, &canvas, &renderer)?;
36    install_mouse(&document, &canvas, &renderer)?;
37    install_raf(&window, &document, &canvas, &renderer);
38
39    Ok(())
40}
41
42fn install_resize(
43    window: &Window,
44    canvas: &HtmlCanvasElement,
45    renderer: &Rc<RefCell<Renderer>>,
46) -> Result<(), JsValue> {
47    let canvas = canvas.clone();
48    let win2 = window.clone();
49    let r = renderer.clone();
50    let cb = Closure::<dyn FnMut()>::new(move || {
51        fit_canvas(&canvas, &win2);
52        r.borrow_mut().resize(canvas.width(), canvas.height());
53    });
54    window.add_event_listener_with_callback("resize", cb.as_ref().unchecked_ref())?;
55    cb.forget();
56    Ok(())
57}
58
59fn install_mouse(
60    document: &Document,
61    canvas: &HtmlCanvasElement,
62    renderer: &Rc<RefCell<Renderer>>,
63) -> Result<(), JsValue> {
64    let canvas = canvas.clone();
65    let r = renderer.clone();
66    let cb = Closure::<dyn FnMut(MouseEvent)>::new(move |e: MouseEvent| {
67        let w = canvas.client_width().max(1) as f32;
68        let h = canvas.client_height().max(1) as f32;
69        // Origen en el centro del canvas, +y arriba.
70        let x = e.client_x() as f32 - w * 0.5;
71        let y = h * 0.5 - e.client_y() as f32;
72        r.borrow_mut().set_mouse_px(x, y);
73    });
74    document.add_event_listener_with_callback("mousemove", cb.as_ref().unchecked_ref())?;
75    cb.forget();
76    Ok(())
77}
78
79fn install_raf(
80    window: &Window,
81    document: &Document,
82    canvas: &HtmlCanvasElement,
83    renderer: &Rc<RefCell<Renderer>>,
84) {
85    let f = Rc::new(RefCell::new(None::<Closure<dyn FnMut(f64)>>));
86    let g = f.clone();
87    let renderer = renderer.clone();
88    let canvas = canvas.clone();
89    let document = document.clone();
90    let window2 = window.clone();
91    *g.borrow_mut() = Some(Closure::<dyn FnMut(f64)>::new(move |time_ms: f64| {
92        renderer.borrow_mut().render(time_ms);
93        position_tips(&document, &canvas, &renderer.borrow());
94        if let Some(cb) = f.borrow().as_ref() {
95            let _ = window2.request_animation_frame(cb.as_ref().unchecked_ref());
96        }
97    }));
98    let _ = window.request_animation_frame(g.borrow().as_ref().unwrap().as_ref().unchecked_ref());
99}
100
101fn fit_canvas(canvas: &HtmlCanvasElement, window: &Window) {
102    let dpr = window.device_pixel_ratio() as f32;
103    let w = window
104        .inner_width()
105        .ok()
106        .and_then(|v| v.as_f64())
107        .unwrap_or(1280.0) as f32;
108    let h = window
109        .inner_height()
110        .ok()
111        .and_then(|v| v.as_f64())
112        .unwrap_or(720.0) as f32;
113    canvas.set_width((w * dpr) as u32);
114    canvas.set_height((h * dpr) as u32);
115    let el: &HtmlElement = canvas.unchecked_ref();
116    let style = el.style();
117    let _ = style.set_property("width", &format!("{}px", w));
118    let _ = style.set_property("height", &format!("{}px", h));
119}
120
121fn position_tips(document: &Document, canvas: &HtmlCanvasElement, renderer: &Renderer) {
122    let clips = renderer.tips_ndc();
123    let cw = canvas.client_width().max(1) as f32;
124    let ch = canvas.client_height().max(1) as f32;
125    for (i, (id, _color, _label)) in tips::ORDER.iter().enumerate() {
126        let (nx, ny) = clips[i];
127        let px = (nx + 1.0) * 0.5 * cw;
128        let py = (1.0 - (ny + 1.0) * 0.5) * ch;
129        let sel = format!("tip-{}", id);
130        if let Some(el) = document.get_element_by_id(&sel) {
131            if let Ok(el) = el.dyn_into::<HtmlElement>() {
132                let _ = el.style().set_property(
133                    "transform",
134                    &format!("translate({:.2}px, {:.2}px) translate(-50%, -50%)", px, py),
135                );
136            }
137        }
138    }
139}
140
141fn install_panic_hook() {
142    static SET: std::sync::Once = std::sync::Once::new();
143    SET.call_once(|| {
144        std::panic::set_hook(Box::new(|info| {
145            let msg = format!("{}", info);
146            web_sys::console::error_1(&JsValue::from_str(&msg));
147        }));
148    });
149}