fix(ui-dioxus): use schedule_update() for cross-thread Blitz animation
This commit is contained in:
parent
7cea86361a
commit
c9f8679744
1 changed files with 14 additions and 22 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
/// PulsingDot — GPU-friendly animated status indicator.
|
/// PulsingDot — GPU-friendly animated status indicator for Blitz renderer.
|
||||||
///
|
///
|
||||||
/// Uses Dioxus use_resource + channel pattern for Blitz-compatible animation.
|
/// Pure OS-thread animation — no async runtime dependency.
|
||||||
/// Blitz/wgpu doesn't optimize CSS animations (full-scene repaint every frame).
|
/// OS thread updates AtomicU32 + calls schedule_update() to trigger re-render.
|
||||||
/// This: OS thread does timing → atomic float → use_resource polls it.
|
/// schedule_update() returns Arc<dyn Fn() + Send + Sync> — safe from any thread.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
@ -22,13 +22,14 @@ pub enum DotState {
|
||||||
pub fn PulsingDot(state: DotState, size: Option<String>) -> Element {
|
pub fn PulsingDot(state: DotState, size: Option<String>) -> Element {
|
||||||
let should_pulse = matches!(state, DotState::Running | DotState::Stalled);
|
let should_pulse = matches!(state, DotState::Running | DotState::Stalled);
|
||||||
|
|
||||||
// Shared atomic for cross-thread opacity (f32 stored as u32 bits)
|
// Shared atomic for cross-thread opacity (f32 as u32 bits)
|
||||||
let atomic_opacity = use_hook(|| Arc::new(AtomicU32::new(f32::to_bits(1.0))));
|
let atomic_opacity = use_hook(|| Arc::new(AtomicU32::new(f32::to_bits(1.0))));
|
||||||
let mut rendered_opacity = use_signal(|| 1.0f32);
|
|
||||||
|
|
||||||
if should_pulse {
|
if should_pulse {
|
||||||
let ao = atomic_opacity.clone();
|
let ao = atomic_opacity.clone();
|
||||||
// Spawn OS thread once to drive animation timing
|
// schedule_update returns Arc<dyn Fn() + Send + Sync> — works from OS threads
|
||||||
|
let updater = use_hook(|| dioxus::dioxus_core::schedule_update());
|
||||||
|
|
||||||
use_hook(move || {
|
use_hook(move || {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let steps = 8u32;
|
let steps = 8u32;
|
||||||
|
|
@ -43,26 +44,13 @@ pub fn PulsingDot(state: DotState, size: Option<String>) -> Element {
|
||||||
0.4 + (t * 0.6)
|
0.4 + (t * 0.6)
|
||||||
};
|
};
|
||||||
ao.store(f32::to_bits(val), Ordering::Relaxed);
|
ao.store(f32::to_bits(val), Ordering::Relaxed);
|
||||||
|
updater(); // Trigger re-render from OS thread
|
||||||
std::thread::sleep(Duration::from_millis(step_ms));
|
std::thread::sleep(Duration::from_millis(step_ms));
|
||||||
}
|
}
|
||||||
going_down = !going_down;
|
going_down = !going_down;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Poll the atomic value and update signal (runs in Dioxus runtime)
|
|
||||||
let ao2 = atomic_opacity.clone();
|
|
||||||
use_future(move || {
|
|
||||||
let ao3 = ao2.clone();
|
|
||||||
async move {
|
|
||||||
loop {
|
|
||||||
let bits = ao3.load(Ordering::Relaxed);
|
|
||||||
let val = f32::from_bits(bits);
|
|
||||||
rendered_opacity.set(val);
|
|
||||||
tokio::time::sleep(Duration::from_millis(60)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (color, shadow) = match state {
|
let (color, shadow) = match state {
|
||||||
|
|
@ -74,7 +62,11 @@ pub fn PulsingDot(state: DotState, size: Option<String>) -> Element {
|
||||||
};
|
};
|
||||||
|
|
||||||
let sz = size.unwrap_or_else(|| "8px".to_string());
|
let sz = size.unwrap_or_else(|| "8px".to_string());
|
||||||
let op = if should_pulse { *rendered_opacity.read() } else { 1.0 };
|
let op = if should_pulse {
|
||||||
|
f32::from_bits(atomic_opacity.load(Ordering::Relaxed))
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
span {
|
span {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue