Skip to content

Commit

Permalink
Merge pull request #3 from kimonp/general_refactor
Browse files Browse the repository at this point in the history
Refactor game of life to be more react like
  • Loading branch information
kimonp authored Dec 26, 2023
2 parents 306053d + fc291e2 commit 13f83e8
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 166 deletions.
6 changes: 3 additions & 3 deletions Dioxus.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ asset_dir = "public"
[web.app]

# HTML title tag content
title = "Dioxus | An elegant GUI library for Rust"
title = "Game of Life | Game of Life example using the Dioxus framework"

[web.watcher]

Expand Down Expand Up @@ -66,9 +66,9 @@ copyright = ""
category = "Utility"

# Bundle short description
short_description = "An amazing dioxus application."
short_description = "Game of Life example."

# Bundle long description
long_description = """
An amazing dioxus application.
Game of life example using the Dioxus framework.
"""
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Dioxus Game Of Life
# Conway's Game Of Life implemented with the Dioxus framework
An implementation of [Conway's game of life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) using the Dioxus framework,
ported from the [rust wasm tutorial](https://rustwasm.github.io/docs/book/game-of-life/introduction.html).

Expand All @@ -7,5 +7,5 @@ While the code from the original tutorial is about 50% rust, 50% JavaScript, wit
## Install and run
* Install cargo and rust
* Install the dioxus API: cargo dioxus install
* Run in debug mode: dx serve --platform=web
* Run in debug mode with the dioxus cli: dx serve --platform=web
* Point your browser at: http://localhost:8080
67 changes: 67 additions & 0 deletions src/animation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//! Animation frame control via a custom Dioxus hook.
use std::cell::RefCell;
use std::rc::Rc;

use dioxus::prelude::*;
use wasm_bindgen::prelude::Closure;

use crate::websys_utils::*;

/// A custom Dioxus hook that abstracts the request_animation_frame() and cancel_animation_frame() DOM calls.
///
/// Allows the caller to create a use_effect which watches the frame_id which can then
/// take an action each time a frame is advanced.
///
/// Returns two UseState variables: frame_running and frame_id.
/// * frame_running is true if frames are advancing.
/// * frame_id is incremented each time a new frame is run.
///
/// If frame_running is set to true, frames advance.
/// If frame_running is set to false, frames stop advancing.
pub fn use_animation_frame(cx: Scope, initial_state: bool) -> (&UseState<bool>, &UseState<i32>) {
let frame_running = use_state(cx, || initial_state);
let cancel_id = use_state(cx, || None::<i32>);
let frame_id = use_state(cx, || 0_i32);

use_effect(cx, (frame_running,), |(frame_running,)| {
to_owned![cancel_id, frame_id, frame_running];

// frame_loop_holder holds a closure that is passed to request_animation_frame().
// This closure is called each time an animation frame completes. We modify the universe
// inside this closure.
let frame_loop_holder = Rc::new(RefCell::new(None));
let frame_loop_holder_clone = frame_loop_holder.clone();

let cancel_id_clone = cancel_id.clone();
*frame_loop_holder.borrow_mut() = Some(Closure::<dyn FnMut()>::new(move || {
let new_id =
request_animation_frame(frame_loop_holder_clone.borrow().as_ref().unwrap());
cancel_id_clone.set(Some(new_id));

frame_id.with_mut(|id| {
*id = id.wrapping_add(1);
})
}));

async move {
// If we are requested to run, but we are not running, run
if *frame_running.get() && cancel_id.get().is_none() {
let new_id = request_animation_frame(frame_loop_holder.borrow().as_ref().unwrap());
cancel_id.set(Some(new_id));
}

// If we are requested to stop, but we are running, cancel
if !*frame_running.get() && cancel_id.get().is_some() {
cancel_id.with_mut(|maybe_id| {
if let Some(id) = maybe_id {
cancel_animation_frame(*id);
*maybe_id = None;
}
});
}
}
});

(frame_running, frame_id)
}
48 changes: 36 additions & 12 deletions src/frames_per_second.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,47 @@
//! Calculates the frames per second and places the text in the given id.
use dioxus::prelude::*;

use crate::websys_utils::window;
use std::collections::VecDeque;
use crate::bindgen_glue::{document, window};

pub struct FramesPerSecond {
// Frames per second component that shows how quickly the app is rendering animation frames.
#[component]
pub fn FramesPerSecond(cx: Scope, frame_id: i32) -> Element {
let frames_per_second = use_ref(cx, FramesPerSecond::new);
let fps_text = use_state(cx, || frames_per_second.read().text());

// console_log!("Running app: {:?}", frame_id.get());

use_effect(cx, (frame_id,), |(_frame_id,)| {
to_owned![frames_per_second, fps_text];
async move {
frames_per_second.with_mut(|fps| {
fps.update_frame();
fps_text.modify(|_old_text| fps.text());
});
}
});

render! {
div { white_space: "pre", font_family: "monospace", fps_text.get().clone() }
}
}

struct FramesPerSecond {
last_timeframe_stamp: f64,
frames: VecDeque<f64>,
performance: web_sys::Performance,
element_id: String,
}

impl Default for FramesPerSecond {
fn default() -> Self {
Self::new()
}
}

impl FramesPerSecond {
pub fn new(element_id: &str) -> FramesPerSecond {
pub fn new() -> FramesPerSecond {
let window = window();
let performance = window
.performance()
Expand All @@ -22,12 +52,11 @@ impl FramesPerSecond {
last_timeframe_stamp: start,
frames: VecDeque::new(),
performance,
element_id: element_id.to_string(),
}
}

/// Display the current calculation for frames per second.
fn text(&self) -> String {
pub fn text(&self) -> String {
let mut sum = 0_f64;
let mut min = f64::MAX;
let mut max = f64::MIN;
Expand Down Expand Up @@ -74,10 +103,5 @@ max of last 100 = {max}
if self.frames.len() > 100 {
self.frames.pop_back();
}

let element_id = &self.element_id;
#[allow(clippy::expect_fun_call)]
let element = document().get_element_by_id(element_id).expect(&format!("Could not find element {element_id}"));
element.set_text_content(Some(&self.text()))
}
}
}
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#[macro_use]
pub mod bindgen_glue;
pub mod websys_utils;
pub mod animation;
pub mod frames_per_second;
pub mod universe;
Loading

0 comments on commit 13f83e8

Please sign in to comment.