|
use wasm_bindgen::prelude::*; |
|
use wasm_bindgen::JsCast; |
|
use web_sys::{window, HtmlInputElement, HtmlElement}; |
|
use console_error_panic_hook; |
|
use std::cell::RefCell; |
|
|
|
// ---------------------- |
|
// PART 1: Shared State |
|
// ---------------------- |
|
|
|
struct SharedState { |
|
data: Vec<f32>, |
|
} |
|
|
|
impl SharedState { |
|
fn new(len: usize) -> Self { |
|
Self { |
|
data: vec![0.0; len], |
|
} |
|
} |
|
|
|
fn set_value(&mut self, index: usize, val: f32) { |
|
self.data[index] = val; |
|
} |
|
|
|
fn get_value(&self, index: usize) -> f32 { |
|
self.data[index] |
|
} |
|
} |
|
|
|
// A single-thread “thread-local” global. Each thread would have its own instance. |
|
// For most Wasm setups, there's only one thread anyway. |
|
thread_local! { |
|
static SHARED_STATE: RefCell<SharedState> = RefCell::new(SharedState::new(10)); |
|
} |
|
|
|
// Helpers to modify/read the state. Use `SHARED_STATE.with(...)`. |
|
fn set_shared_value(index: usize, val: f32) { |
|
SHARED_STATE.with(|cell| { |
|
cell.borrow_mut().set_value(index, val); |
|
}); |
|
} |
|
|
|
fn get_shared_value(index: usize) -> f32 { |
|
SHARED_STATE.with(|cell| cell.borrow().get_value(index)) |
|
} |
|
|
|
// ---------------------- |
|
// PART 2: Startup Code |
|
// ---------------------- |
|
|
|
#[wasm_bindgen(start)] |
|
pub fn main_js() -> Result<(), JsValue> { |
|
console_error_panic_hook::set_once(); |
|
|
|
// Let's store an initial value to prove it's working |
|
set_shared_value(0, 50.0); |
|
|
|
// 1) Grab document & body |
|
let document = window() |
|
.and_then(|win| win.document()) |
|
.ok_or("failed to get document")?; |
|
let output_div = document |
|
.get_element_by_id("output") |
|
.ok_or("no #output element")? |
|
.dyn_into::<HtmlElement>()?; |
|
|
|
// 2) Grab the slider |
|
let slider = document |
|
.get_element_by_id("slider") |
|
.ok_or("no #slider element")? |
|
.dyn_into::<HtmlInputElement>()?; |
|
|
|
// Clone them for the closure |
|
let slider_clone = slider.clone(); |
|
let output_div_clone = output_div.clone(); |
|
|
|
// 3) Create a closure that updates the shared array and the DOM |
|
let closure = Closure::wrap(Box::new(move || { |
|
let val = slider_clone.value().parse::<f32>().unwrap_or(0.0); |
|
set_shared_value(0, val); |
|
let stored = get_shared_value(0); |
|
output_div_clone.set_inner_text(&format!("Stored value is: {:.2}", stored)); |
|
}) as Box<dyn FnMut()>); |
|
|
|
// 4) Attach the closure as input event listener |
|
slider.add_event_listener_with_callback("input", closure.as_ref().unchecked_ref())?; |
|
// keep closure alive |
|
closure.forget(); |
|
|
|
// 5) Run it once at startup |
|
slider.dispatch_event(&web_sys::Event::new("input")?)?; |
|
|
|
Ok(()) |
|
} |