如何将本地闭包转换为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的答案中所示的略短。它改编自此示例:
let closure: Closure<dyn Fn()> = Closure::new(move || {
// Do something here
});
your_html_element.set_oninput(Some(closure.as_ref().unchecked_ref()));
closure.forget();
下面是一个更完整的示例,演示如何在文件选取器上设置回调。回调访问 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());
}