1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -04:00

Fix crash on request_animation_frame when destroying web runner (#4169)

Previously, any frames in flight (`requestAnimationFrame`) on web were
not being cancelled (`cancelAnimationFrame`) when `WebRunner::destroy`
was called. If a user called `destroy`, then immediately removed the
canvas from the DOM, eframe could panic with a "failed to find (canvas)
element by id" error message.

This PR changes two things:
- The canvas element is directly referenced everywhere it's needed
instead of being looked up by `canvas_id`[^1]
- The RAF handle is stored in `WebRunner` and `cancelAnimationFrame` is
called on it inside of `WebRunner::destroy`[^2]

[^1]: The WebGL/WGPU backends were already holding onto the canvas (and
associated GPU context), so the change is just converting all the
`get_element_by_id` lookups to retrieve the canvas from the web runner
handle.

[^2]: There is only ever one frame in flight, so we store it directly as
a scalar field.
This commit is contained in:
Jan Procházka
2024-03-14 10:26:34 +01:00
committed by GitHub
parent 00e8ce6d7e
commit c5eaba43cd
9 changed files with 73 additions and 61 deletions

View File

@@ -100,26 +100,23 @@ fn theme_from_dark_mode(dark_mode: bool) -> Theme {
}
}
fn canvas_element(canvas_id: &str) -> Option<web_sys::HtmlCanvasElement> {
fn get_canvas_element_by_id(canvas_id: &str) -> Option<web_sys::HtmlCanvasElement> {
let document = web_sys::window()?.document()?;
let canvas = document.get_element_by_id(canvas_id)?;
canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
}
fn canvas_element_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElement {
canvas_element(canvas_id)
fn get_canvas_element_by_id_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElement {
get_canvas_element_by_id(canvas_id)
.unwrap_or_else(|| panic!("Failed to find canvas with id {canvas_id:?}"))
}
fn canvas_origin(canvas_id: &str) -> egui::Pos2 {
let rect = canvas_element(canvas_id)
.unwrap()
.get_bounding_client_rect();
fn canvas_origin(canvas: &web_sys::HtmlCanvasElement) -> egui::Pos2 {
let rect = canvas.get_bounding_client_rect();
egui::pos2(rect.left() as f32, rect.top() as f32)
}
fn canvas_size_in_points(canvas_id: &str) -> egui::Vec2 {
let canvas = canvas_element(canvas_id).unwrap();
fn canvas_size_in_points(canvas: &web_sys::HtmlCanvasElement) -> egui::Vec2 {
let pixels_per_point = native_pixels_per_point();
egui::vec2(
canvas.width() as f32 / pixels_per_point,
@@ -127,8 +124,10 @@ fn canvas_size_in_points(canvas_id: &str) -> egui::Vec2 {
)
}
fn resize_canvas_to_screen_size(canvas_id: &str, max_size_points: egui::Vec2) -> Option<()> {
let canvas = canvas_element(canvas_id)?;
fn resize_canvas_to_screen_size(
canvas: &web_sys::HtmlCanvasElement,
max_size_points: egui::Vec2,
) -> Option<()> {
let parent = canvas.parent_element()?;
// Prefer the client width and height so that if the parent