On Web, use requestAnimationFrame for RedrawRequested

This commit is contained in:
dAxpeDDa 2023-06-23 16:21:41 +02:00 committed by Kirill Chibisov
parent 7a58fe58ce
commit 57fad2ce15
7 changed files with 87 additions and 10 deletions

View file

@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre
# Unreleased # Unreleased
- On Web, use `Window.requestAnimationFrame()` to throttle `RedrawRequested` events.
- On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor. - On Wayland, use frame callbacks to throttle `RedrawRequested` events so redraws will align with compositor.
- Add `Window::pre_present_notify` to notify winit before presenting to the windowing system. - Add `Window::pre_present_notify` to notify winit before presenting to the windowing system.
- On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name. - On Windows, added `WindowBuilderExtWindows::with_class_name` to customize the internal class name.

View file

@ -735,7 +735,10 @@ impl<T> EventLoopWindowTarget<T> {
} }
canvas_clone.borrow_mut().is_intersecting = Some(is_intersecting); canvas_clone.borrow_mut().is_intersecting = Some(is_intersecting);
}) });
let runner = self.runner.clone();
canvas.on_animation_frame(move || runner.request_redraw(RootWindowId(id)));
} }
pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> { pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {

View file

@ -0,0 +1,62 @@
use std::cell::Cell;
use std::rc::Rc;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
pub struct AnimationFrameHandler {
window: web_sys::Window,
closure: Closure<dyn FnMut()>,
handle: Rc<Cell<Option<i32>>>,
}
impl AnimationFrameHandler {
pub fn new(window: web_sys::Window) -> Self {
let handle = Rc::new(Cell::new(None));
let closure = Closure::new({
let handle = handle.clone();
move || handle.set(None)
});
Self {
window,
closure,
handle,
}
}
pub fn on_animation_frame<F>(&mut self, mut f: F)
where
F: 'static + FnMut(),
{
let handle = self.handle.clone();
self.closure = Closure::new(move || {
handle.set(None);
f();
})
}
pub fn request(&self) {
if let Some(handle) = self.handle.take() {
self.window
.cancel_animation_frame(handle)
.expect("Failed to cancel animation frame");
}
let handle = self
.window
.request_animation_frame(self.closure.as_ref().unchecked_ref())
.expect("Failed to request animation frame");
self.handle.set(Some(handle));
}
}
impl Drop for AnimationFrameHandler {
fn drop(&mut self) {
if let Some(handle) = self.handle.take() {
self.window
.cancel_animation_frame(handle)
.expect("Failed to cancel animation frame");
}
}
}

View file

@ -19,6 +19,7 @@ use crate::platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes};
use crate::window::{WindowAttributes, WindowId as RootWindowId}; use crate::window::{WindowAttributes, WindowId as RootWindowId};
use super::super::WindowId; use super::super::WindowId;
use super::animation_frame::AnimationFrameHandler;
use super::event_handle::EventListenerHandle; use super::event_handle::EventListenerHandle;
use super::intersection_handle::IntersectionObserverHandle; use super::intersection_handle::IntersectionObserverHandle;
use super::media_query_handle::MediaQueryListHandle; use super::media_query_handle::MediaQueryListHandle;
@ -42,6 +43,7 @@ pub struct Canvas {
pointer_handler: PointerHandler, pointer_handler: PointerHandler,
on_resize_scale: Option<ResizeScaleHandle>, on_resize_scale: Option<ResizeScaleHandle>,
on_intersect: Option<IntersectionObserverHandle>, on_intersect: Option<IntersectionObserverHandle>,
animation_frame_handler: AnimationFrameHandler,
} }
pub struct Common { pub struct Common {
@ -98,7 +100,7 @@ impl Canvas {
.expect("Invalid pseudo-element"); .expect("Invalid pseudo-element");
let common = Common { let common = Common {
window, window: window.clone(),
document, document,
raw: canvas, raw: canvas,
style, style,
@ -151,6 +153,7 @@ impl Canvas {
pointer_handler: PointerHandler::new(), pointer_handler: PointerHandler::new(),
on_resize_scale: None, on_resize_scale: None,
on_intersect: None, on_intersect: None,
animation_frame_handler: AnimationFrameHandler::new(window),
}) })
} }
@ -441,6 +444,13 @@ impl Canvas {
self.on_intersect = Some(IntersectionObserverHandle::new(self.raw(), handler)); self.on_intersect = Some(IntersectionObserverHandle::new(self.raw(), handler));
} }
pub(crate) fn on_animation_frame<F>(&mut self, f: F)
where
F: 'static + FnMut(),
{
self.animation_frame_handler.on_animation_frame(f)
}
pub fn request_fullscreen(&self) { pub fn request_fullscreen(&self) {
self.common.request_fullscreen() self.common.request_fullscreen()
} }
@ -449,6 +459,10 @@ impl Canvas {
self.common.is_fullscreen() self.common.is_fullscreen()
} }
pub fn request_animation_frame(&self) {
self.animation_frame_handler.request();
}
pub(crate) fn handle_scale_change<T: 'static>( pub(crate) fn handle_scale_change<T: 'static>(
&self, &self,
runner: &super::super::event_loop::runner::Shared<T>, runner: &super::super::event_loop::runner::Shared<T>,

View file

@ -1,3 +1,4 @@
mod animation_frame;
mod canvas; mod canvas;
pub mod event; pub mod event;
mod event_handle; mod event_handle;

View file

@ -30,7 +30,6 @@ pub struct Inner {
document: Document, document: Document,
canvas: Rc<RefCell<backend::Canvas>>, canvas: Rc<RefCell<backend::Canvas>>,
previous_pointer: RefCell<&'static str>, previous_pointer: RefCell<&'static str>,
register_redraw_request: Box<dyn Fn()>,
destroy_fn: Option<Box<dyn FnOnce()>>, destroy_fn: Option<Box<dyn FnOnce()>>,
} }
@ -40,8 +39,6 @@ impl Window {
attr: WindowAttributes, attr: WindowAttributes,
platform_attr: PlatformSpecificWindowBuilderAttributes, platform_attr: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOE> { ) -> Result<Self, RootOE> {
let runner = target.runner.clone();
let id = target.generate_id(); let id = target.generate_id();
let prevent_default = platform_attr.prevent_default; let prevent_default = platform_attr.prevent_default;
@ -52,8 +49,6 @@ impl Window {
backend::Canvas::create(id, window.clone(), document.clone(), &attr, platform_attr)?; backend::Canvas::create(id, window.clone(), document.clone(), &attr, platform_attr)?;
let canvas = Rc::new(RefCell::new(canvas)); let canvas = Rc::new(RefCell::new(canvas));
let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id)));
target.register(&canvas, id, prevent_default); target.register(&canvas, id, prevent_default);
let runner = target.runner.clone(); let runner = target.runner.clone();
@ -68,7 +63,6 @@ impl Window {
document: document.clone(), document: document.clone(),
canvas, canvas,
previous_pointer: RefCell::new("auto"), previous_pointer: RefCell::new("auto"),
register_redraw_request,
destroy_fn: Some(destroy_fn), destroy_fn: Some(destroy_fn),
}) })
.unwrap(), .unwrap(),
@ -110,8 +104,9 @@ impl Window {
} }
pub fn request_redraw(&self) { pub fn request_redraw(&self) {
self.inner self.inner.dispatch(move |inner| {
.dispatch(|inner| (inner.register_redraw_request)()); inner.canvas.borrow().request_animation_frame();
});
} }
pub fn pre_present_notify(&self) {} pub fn pre_present_notify(&self) {}

View file

@ -547,6 +547,7 @@ impl Window {
/// - **iOS:** Can only be called on the main thread. /// - **iOS:** Can only be called on the main thread.
/// - **Wayland:** The events are aligned with the frame callbacks when [`Window::pre_present_notify`] /// - **Wayland:** The events are aligned with the frame callbacks when [`Window::pre_present_notify`]
/// is used. /// is used.
/// - **Web:** [`Event::RedrawRequested`] will be aligned with the `requestAnimationFrame`.
/// ///
/// [`Event::RedrawRequested`]: crate::event::Event::RedrawRequested /// [`Event::RedrawRequested`]: crate::event::Event::RedrawRequested
#[inline] #[inline]