1use dioxus::prelude::*;
2
3use crate::runtime::{SplineApplication, SplineEvent, SplineEventName};
4use wasm_bindgen::{JsCast, JsValue};
5
6#[derive(Props, PartialEq, Clone)]
7pub struct SplineProps {
8 #[props(into)]
9 pub scene: String,
10 pub on_load: Option<EventHandler<SplineApplication>>,
11 pub on_mouse_down: Option<EventHandler<SplineEvent>>,
12 pub on_mouse_up: Option<EventHandler<SplineEvent>>,
13 pub on_mouse_hover: Option<EventHandler<SplineEvent>>,
14 pub on_key_down: Option<EventHandler<SplineEvent>>,
15 pub on_key_up: Option<EventHandler<SplineEvent>>,
16 pub on_start: Option<EventHandler<SplineEvent>>,
17 pub on_look_at: Option<EventHandler<SplineEvent>>,
18 pub on_follow: Option<EventHandler<SplineEvent>>,
19 pub on_wheel: Option<EventHandler<SplineEvent>>,
20 pub render_on_demand: Option<bool>,
21}
22
23fn get_raw_canvas_element(mounted: &MountedData) -> &web_sys::HtmlCanvasElement {
25 mounted
26 .downcast::<web_sys::Element>()
27 .unwrap()
28 .dyn_ref::<web_sys::HtmlCanvasElement>()
29 .unwrap()
30}
31
32fn _event_factory(
33 props: &SplineProps,
34) -> Vec<(SplineEventName, Option<EventHandler<SplineEvent>>)> {
35 vec![
36 (SplineEventName::mouseDown, props.on_mouse_down),
37 (SplineEventName::mouseUp, props.on_mouse_up),
38 (SplineEventName::mouseHover, props.on_mouse_hover),
39 (SplineEventName::keyDown, props.on_key_down),
40 (SplineEventName::keyUp, props.on_key_up),
41 (SplineEventName::start, props.on_start),
42 (SplineEventName::lookAt, props.on_look_at),
43 (SplineEventName::follow, props.on_follow),
44 (SplineEventName::scroll, props.on_wheel),
45 ]
46}
47#[component]
85pub fn Spline(props: SplineProps) -> Element {
86 let mut app = use_signal(|| None::<SplineApplication>);
87 let scene = use_signal(|| props.scene.clone());
88 let props_cloned = props.clone();
89 let mut is_loading = use_signal(|| true);
90 let _ = use_resource(move || {
92 let events = _event_factory(&props_cloned);
93
94 async move {
95 app.unwrap().load(scene()).await;
96
97 is_loading.set(false);
98 for (event_name, handler) in events {
99 if let Some(handler) = handler {
100 let cb = move |event: JsValue| {
101 let event: SplineEvent = event.into();
102 handler.call(event);
103 };
104
105 tracing::info!("Adding event listener for {:?}", event_name);
106 app.unwrap()
107 .add_event_listener(event_name.to_string().as_str(), cb);
108 }
109 }
110 if let Some(on_load) = props_cloned.on_load {
111 on_load.call(app.unwrap())
112 }
113 }
114 });
115
116 rsx! {
117 canvas {
118 onmounted: move |event: Event<MountedData>| {
119 let canvas_ref = get_raw_canvas_element(&event.data);
120 let render_on_demand = props.render_on_demand.unwrap_or(true);
121 app.set(Some(SplineApplication::new(canvas_ref, render_on_demand)));
122 },
123 style: match is_loading() {
124 true => "display: none;",
125 false => "display: block; width: 100%; height: 100%;",
126 }
127 }
128 }
129}