Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of gloo_render::request_animation_frame #319

Open
tad-lispy opened this issue Mar 7, 2023 · 2 comments
Open

Example of gloo_render::request_animation_frame #319

tad-lispy opened this issue Mar 7, 2023 · 2 comments
Labels
question Further information is requested

Comments

@tad-lispy
Copy link

Hi! Thank you for all your efforts around Gloo. It's very nice.

I'm heaving trouble figuring out how to use gloo_render::request_animation_frame and would really appreciate a simple, yet practical example. Probably I'm just missing something basic, as I am quite inexperienced with Rust. An example could go a long way. Below are a few things I tried so far.

The simplest approach, basically as I would do it in JS.

fn main() {
    request_animation_frame(step);
}

fn step(timestamp: f64) {
    console::log!("Frame", timestamp);
    request_animation_frame(step);
}

This compiles, but produces no output. My understanding is that as soon as the reference to an AnimationFrame goes out of scope, the request is cancelled and the step callback never called. So the solution has to somehow keep the reference alive. With this in mind I tried the following (in context of a Yew app):

pub struct App {
    animation: Option<AnimationFrame>,
    // ...
}

impl Component for App {
    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            Message::FormSubmitted => {
                self.animation = Some(request_animation_frame(|timestamp| {
                    console::log!("First frame", timestamp);
                    ctx.link().send_message(Message::Frame(timestamp));
                }));
            },
            
            Message::Frame(previous) => {
                console::log!("Frame message", previous);
                self.animation = request_animation_frame(move |timestamp| {
                    console::log!("Next frame duration ", timestamp - previous);
                    ctx.link().send_message(Message::Frame(timestamp));
                })
                .into();
            }

        }
        // rest of the update...
    }
    // rest of the impl...
}

I'm getting the following:

error[E0521]: borrowed data escapes outside of associated function
   --> src/ui.rs:137:38
    |
121 |       fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
    |                            ---  - let's call the lifetime of this reference `'1`
    |                            |
    |                            `ctx` is a reference that is only valid in the associated function body
...
137 |                       self.animation = request_animation_frame(|timestamp| {
    |  ______________________________________^
138 | |                         console::log!("First frame", timestamp);
139 | |                         ctx.link().send_message(Message::Frame(timestamp));
140 | |                     }).into();
    | |                      ^
    | |                      |
    | |______________________`ctx` escapes the associated function body here
    |                        argument requires that `'1` must outlive `'static`

This example is Yew specific, so probably only good for demonstration. With a basic example, I hopefully be able to integrate it with my Yew code.

@tad-lispy tad-lispy added the bug Something isn't working label Mar 7, 2023
@ranile
Copy link
Collaborator

ranile commented Mar 7, 2023

You need to clone the output of ctx.link() and move it into the closure passed to request_animation_frame

@ranile ranile added question Further information is requested and removed bug Something isn't working labels Mar 7, 2023
@tad-lispy
Copy link
Author

Thank you!

In the mean time I dug up a two years old message on Discord and got it to work with ctx.link().callback() and a Box, holding the AnimationFrame in the component state, like this:

Message::Frame(timestamp, duration) => {
    self.svg.animate(&duration); // This is the actual work that needs to be done in the frame.

    let callback = ctx.link().callback(identity);
    let previous_timestamp = timestamp;
    self.animation = Some(Box::from(request_animation_frame(move |timestamp| {
        let duration = Duration::from_millis((timestamp - previous_timestamp) as u64);
        let msg = Message::Frame(timestamp, duration);
        callback.emit(msg);
    })));
}

Do you think it's right? If so, maybe I'll try to write an example. At least two people asked for an example of using RAF, and I never wrote one before, so it couldl be a nice new challenge :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants