fix: made view port reactive with scaling

This commit is contained in:
Evan Carroll 2026-01-18 02:25:52 -06:00
parent b361460485
commit 8201092703

View file

@ -106,10 +106,11 @@ pub fn RealmSceneViewer(
let outer_container_ref = NodeRef::<leptos::html::Div>::new();
// Store scale factors for coordinate conversion (shared between both canvases)
let scale_x = StoredValue::new(1.0_f64);
let scale_y = StoredValue::new(1.0_f64);
let offset_x = StoredValue::new(0.0_f64);
let offset_y = StoredValue::new(0.0_f64);
// Using RwSignal for reactivity - derived signals will recompute on resize
let (scale_x, set_scale_x) = signal(1.0_f64);
let (scale_y, set_scale_y) = signal(1.0_f64);
let (offset_x, set_offset_x) = signal(0.0_f64);
let (offset_y, set_offset_y) = signal(0.0_f64);
// Signal to track when scale factors have been properly calculated
let (scales_ready, set_scales_ready) = signal(false);
@ -129,10 +130,10 @@ pub fn RealmSceneViewer(
let click_x = ev.client_x() as f64 - rect.left();
let click_y = ev.client_y() as f64 - rect.top();
let sx = scale_x.get_value();
let sy = scale_y.get_value();
let ox = offset_x.get_value();
let oy = offset_y.get_value();
let sx = scale_x.get();
let sy = scale_y.get();
let ox = offset_x.get();
let oy = offset_y.get();
if sx > 0.0 && sy > 0.0 {
let scene_x = (click_x - ox) / sx;
@ -176,7 +177,7 @@ pub fn RealmSceneViewer(
// =========================================================
// Viewport Dimensions Effect - tracks outer container size
// Uses setTimeout to ensure DOM is ready after mount
// Uses window resize event to detect size changes
// =========================================================
Effect::new(move |_| {
// Track pan mode to re-run when it changes (affects container layout)
@ -188,46 +189,69 @@ pub fn RealmSceneViewer(
let container_el: web_sys::HtmlElement = container.into();
// Use setTimeout to ensure DOM has settled after any layout changes
let update_dimensions = Closure::once(Box::new(move || {
// Measure and update dimensions
let measure_container = {
let container_el = container_el.clone();
move || {
let width = container_el.client_width() as f64;
let height = container_el.client_height() as f64;
if width > 0.0 && height > 0.0 {
set_viewport_dimensions.set((width, height));
}
}) as Box<dyn FnOnce()>);
}
};
// Measure immediately
measure_container();
// Also measure on window resize
let resize_handler = Closure::wrap(Box::new({
let container_el = container_el.clone();
move |_: web_sys::Event| {
let width = container_el.client_width() as f64;
let height = container_el.client_height() as f64;
if width > 0.0 && height > 0.0 {
set_viewport_dimensions.set((width, height));
}
}
}) as Box<dyn Fn(web_sys::Event)>);
let window = web_sys::window().unwrap();
let _ = window.set_timeout_with_callback_and_timeout_and_arguments_0(
update_dimensions.as_ref().unchecked_ref(),
150, // Slightly longer delay to ensure DOM settles
let _ = window.add_event_listener_with_callback(
"resize",
resize_handler.as_ref().unchecked_ref(),
);
update_dimensions.forget();
// Keep the closure alive
resize_handler.forget();
});
// Track the last settings to detect changes
let last_pan_mode = Rc::new(RefCell::new(None::<bool>));
let last_zoom = Rc::new(RefCell::new(None::<f64>));
let last_viewport = Rc::new(RefCell::new(None::<(f64, f64)>));
// =========================================================
// Background Effect - redraws when settings change
// Background Effect - redraws when settings or viewport change
// =========================================================
Effect::new(move |_| {
// Track settings signals - this Effect reruns when they change
let current_pan_mode = is_pan_mode.get();
let current_zoom = zoom_level.get();
let current_viewport = viewport_dimensions.get();
let Some(canvas) = bg_canvas_ref.get() else {
return;
};
// Check if we need to redraw (settings changed or first render)
// Check if we need to redraw (settings or viewport changed, or first render)
let needs_redraw = {
let last_pan = *last_pan_mode.borrow();
let last_z = *last_zoom.borrow();
let last_vp = *last_viewport.borrow();
last_pan != Some(current_pan_mode)
|| (current_pan_mode && last_z != Some(current_zoom))
|| (!current_pan_mode && last_vp != Some(current_viewport))
};
if !needs_redraw {
@ -237,6 +261,7 @@ pub fn RealmSceneViewer(
// Update last values
*last_pan_mode.borrow_mut() = Some(current_pan_mode);
*last_zoom.borrow_mut() = Some(current_zoom);
*last_viewport.borrow_mut() = Some(current_viewport);
let canvas_el: &web_sys::HtmlCanvasElement = &canvas;
let canvas_el = canvas_el.clone();
@ -254,10 +279,10 @@ pub fn RealmSceneViewer(
canvas_el.set_height(canvas_height);
// Store scale factors (zoom level, no offset)
scale_x.set_value(current_zoom);
scale_y.set_value(current_zoom);
offset_x.set_value(0.0);
offset_y.set_value(0.0);
set_scale_x.set(current_zoom);
set_scale_y.set(current_zoom);
set_offset_x.set(0.0);
set_offset_y.set(0.0);
// Signal that scale factors are ready
set_scales_ready.set(true);
@ -323,10 +348,10 @@ pub fn RealmSceneViewer(
// Store scale factors
let sx = draw_width / scene_width_f;
let sy = draw_height / scene_height_f;
scale_x.set_value(sx);
scale_y.set_value(sy);
offset_x.set_value(draw_x);
offset_y.set_value(draw_y);
set_scale_x.set(sx);
set_scale_y.set(sy);
set_offset_x.set(draw_x);
set_offset_y.set(draw_y);
// Signal that scale factors are ready
set_scales_ready.set(true);
@ -410,10 +435,10 @@ pub fn RealmSceneViewer(
ctx.clear_rect(0.0, 0.0, canvas_width as f64, canvas_height as f64);
// Get stored scale factors
let sx = scale_x.get_value();
let sy = scale_y.get_value();
let ox = offset_x.get_value();
let oy = offset_y.get_value();
let sx = scale_x.get();
let sy = scale_y.get();
let ox = offset_x.get();
let oy = offset_y.get();
// Calculate prop size based on mode
let prop_size = calculate_prop_size(
@ -715,8 +740,8 @@ pub fn RealmSceneViewer(
let current_pan_mode = is_pan_mode.get();
let current_zoom = zoom_level.get();
let current_enlarge = enlarge_props.get();
let sx = scale_x.get_value();
let sy = scale_y.get_value();
let sx = scale_x.get();
let sy = scale_y.get();
// Reference scale factor for "enlarge props" mode
let ref_scale = (scene_width_f / REFERENCE_WIDTH).max(scene_height_f / REFERENCE_HEIGHT);
@ -738,10 +763,10 @@ pub fn RealmSceneViewer(
let text_em_size = Signal::derive(move || settings.get().text_em_size);
// Create signals for scale/offset values to pass to AvatarCanvas
let scale_x_signal = Signal::derive(move || scale_x.get_value());
let scale_y_signal = Signal::derive(move || scale_y.get_value());
let offset_x_signal = Signal::derive(move || offset_x.get_value());
let offset_y_signal = Signal::derive(move || offset_y.get_value());
let scale_x_signal = Signal::derive(move || scale_x.get());
let scale_y_signal = Signal::derive(move || scale_y.get());
let offset_x_signal = Signal::derive(move || offset_x.get());
let offset_y_signal = Signal::derive(move || offset_y.get());
// Create a map of members by key for efficient lookup
let members_by_key = Signal::derive(move || {