js_sys::Function
?
我想做这样的事情:
let canvas = document.get_element_by_id("canvas").unwrap();
let e: web_sys::HtmlElement = canvas.dyn_into().unwrap();
let f = || {};
e.set_onresize(Some(&f.into()));
我找到了解决方案在这里:
let f = Closure::wrap(Box::new(move || { /* whatever */}) as Box<dyn FnMut()>);
e.set_onresize(Some(f.as_ref().unchecked_ref()));
f.forget(); // It is not good practice, just for simplification!
wasm_bindgen::closure::Closure
页面上找到。
use wasm_bindgen::{closure::Closure, JsCast};
let cb = Closure::new(|| { ... });
let cb = cb.as_ref().unchecked_ref();
以下将闭包设置为回调的方法比Hossein Noroozpour 的回答中所示的方法略短。它改编自这个example:
let closure: Closure<dyn Fn()> = Closure::new(move || {
// Do something here
});
your_html_element.set_oninput(Some(closure.as_ref().unchecked_ref()));
closure.forget();
这里有一个更完整的示例,展示了如何在 file picker 上设置回调。回调访问一个 JavaScript worker:
let document = window().unwrap().document().unwrap();
let worker = Worker::new("./worker.js").unwrap();
let file_picker_elem_id = "file_picker";
match get_input_element(file_picker_elem_id, &document) {
Some(file_picker) => {
// We need "move" to access the worker inside the closure
let closure: Closure<dyn Fn()> = Closure::new(move || {
log_to_browser(format!(
"The file picker callback. Worker: {worker:?}"
));
});
file_picker.set_oninput(Some(closure.as_ref().unchecked_ref()));
closure.forget();
}
None => log_to_browser(format!(
"Couldn't get file picker. Used \"{file_picker_elem_id}\" as its ID"
)),
}
/// Gets an [[HtmlInputElement]] by its `elem_id`.
fn get_input_element(elem_id: &str, document: &Document) -> Option<HtmlInputElement> {
document
.get_element_by_id(elem_id)
.and_then(|elem| elem.dyn_ref::<HtmlInputElement>().map(ToOwned::to_owned))
}
fn log_to_browser(log_msg: String) {
console::log_1(&log_msg.into());
}
我试着简化闭包到
js_sys::Function
s 的转换:
pub fn to_js_func<F>(closure: F) -> Option<&'static js_sys::Function>
where
F: IntoWasmClosure<dyn Fn()> + 'static,
{
let big_closure: Closure<dyn Fn()> = Closure::new(closure);
let func: &js_sys::Function = big_closure.as_ref().unchecked_ref();
big_closure.forget();
Some(func)
}
但这不起作用,因为函数拥有创建的闭包。此外,要使用
js_sys::Function
类型,我需要将 js-sys
依赖项添加到 Cargo.toml。如果有人知道解决这个问题的方法,请告诉我们。
一个不太通用的辅助函数:
fn set_on_input<F>(input_elem: &HtmlInputElement, closure: F)
where
F: IntoWasmClosure<dyn Fn()> + 'static,
{
let big_closure: Closure<dyn Fn()> = Closure::new(closure);
input_elem.set_oninput(Some(big_closure.as_ref().unchecked_ref()));
big_closure.forget();
}
这允许将 Rust 闭包设置为 HTML 元素上的回调:
let closure = move || {
log_to_browser(format!(
"The file picker callback. Worker: {worker:?}"
));
};
set_on_input(&file_picker, closure);
除了闭包,你还可以设置一个函数作为回调:
set_on_input(&file_picker, test_callback);
其中
test_callback
只是一个普通的 Rust 函数:
fn test_callback() {
log_to_browser("test_callback 🥳".into());
}