Adding Interactivity¶
We will continue to explore the JavaScript and WebAssembly interface by adding some interactive features to our Game of Life implementation. We will enable users to toggle whether a cell is alive or dead by clicking on it, and allow pausing the game, which makes drawing cell patterns a lot easier.
Pausing and Resuming the Game¶
Let's add a button to toggle whether the game is playing or paused. To wasm-game-of-life/www/index.html
, add the button right above the <canvas>
:
1 |
|
In the wasm-game-of-life/www/index.js
JavaScript, we will make the following changes:
-
Keep track of the identifier returned by the latest call to
requestAnimationFrame
, so that we can cancel the animation by callingcancelAnimationFrame
with that identifier. -
When the play/pause button is clicked, check for whether we have the identifier for a queued animation frame. If we do, then the game is currently playing, and we want to cancel the animation frame so that
renderLoop
isn't called again, effectively pausing the game. If we do not have an identifier for a queued animation frame, then we are currently paused, and we would like to callrequestAnimationFrame
to resume the game.
Because the JavaScript is driving the Rust and WebAssembly, this is all we need to do, and we don't need to change the Rust sources.
We introduce the animationId
variable to keep track of the identifier returned by requestAnimationFrame
. When there is no queued animation frame, we set this variable to null
.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
At any instant in time, we can tell whether the game is paused or not by inspecting the value of animationId
:
1 2 3 |
|
Now, when the play/pause button is clicked, we check whether the game is currently paused or playing, and resume the renderLoop
animation or cancel the next animation frame respectively. Additionally, we update the button's text icon to reflect the action that the button will take when clicked next.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Finally, we were previously kick-starting the game and its animation by calling requestAnimationFrame(renderLoop)
directly, but we want to replace that with a call to play
so that the button gets the correct initial text icon.
1 2 |
|
Refresh http://localhost:8080/ and we should now be able to pause and resume the game by clicking on the button!
Toggling a Cell's State on "click"
Events¶
Now that we can pause the game, it's time to add the ability to mutate the cells by clicking on them.
To toggle a cell is to flip its state from alive to dead or from dead to alive. Add a toggle
method to Cell
in wasm-game-of-life/src/lib.rs
:
1 2 3 4 5 6 7 8 |
|
To toggle the state of a cell at given row and column, we translate the row and column pair into an index into the cells vector and call the toggle method on the cell at that index:
1 2 3 4 5 6 7 8 9 10 |
|
This method is defined within the impl
block that is annotated with #[wasm_bindgen]
so that it can be called by JavaScript.
In wasm-game-of-life/www/index.js
, we listen to click events on the <canvas>
element, translate the click event's page-relative coordinates into canvas-relative coordinates, and then into a row and column, invoke the toggle_cell
method, and finally redraw the scene.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Rebuild with wasm-pack build
in wasm-game-of-life
, then refresh http://localhost:8080/ again and we can now draw our own patterns by clicking on the cells and toggling their state.
Exercises¶
-
Introduce an
<input type="range">
widget to control how many ticks occur per animation frame. -
Add a button that resets the universe to a random initial state when clicked. Another button that resets the universe to all dead cells.
-
On
Ctrl + Click
, insert a glider centered on the target cell. OnShift + Click
, insert a pulsar.