Questions, comments, complaints?
Things that are not available from Elm, e.g. the local storage or web audio APIs.
localStorage
come from?localStorage
is a property of the window
object that is always available in JavaScript running in the browser. When a variable isn't found in the current scope in JavaScript, it looks to see if it's a property on the window
object (which is basically just all the globals). So, localStorage
is just shorthand for window.localStorage
.
The More Random Elm notes showed one approach, defining one incoming and one outgoing port per JavaScript functionality to wrap.
Alternatively, you might want to have one incoming port and one outgoing port per module that can handle all the messages you need (rather than one for each different functionality you want to implement). The code on the JavaScript side would then have its own "update" function, analogous to update
on the Elm side. You might enjoy this talk on The Importance of Ports.
We'll work in randomly assigned Zoom breakout rooms for ~60 minutes. Work individually and discuss questions and issues that come up.
Did you play typing tutor games as a kid to hone your keyboard skills? Do you regularly test yourself to maintain your form? (After all, we are typists first — h/t Norman Ramsey.) But have you ever played clicking games to hone your mouse skills? If not, here is your chance to address a deficiency in your computer education. Aim for the center of the red target.
Now you'll implement this (really lame) game as an excuse to get a bit more practice with web programming in Elm, to help as you work on HW2 and as you brainstorm project ideas in the coming weeks.
We'll break this down into a series of milestones, filling in the holes in MouseGame.elm
and MouseGame.html
(look for the TODO
s). Try to keep things clicking — compiling and running — as you make incremental progress towards the final version. (A couple bugs in the starter code were fixed and uploaded during class.)
Look at how much of the application design can be discerned just from the type signatures!
type alias Model =
{ windowWidth : Int
, windowHeight : Int
, nextTarget : Maybe Point
, lastClick : Maybe { point : Point, distanceToLastTarget : Float }
, showCenter : Bool
, targetRadius : Int
}
type alias Flags =
{ windowWidth : Int
, windowHeight : Int
}
type Msg
= Resize Int Int
| MouseDown Point
| RandomPoint Point
| ToggleShowCenter
| SetTargetRadius Int
To start:
% elm init
% elm install mdgriffith/elm-ui
% elm install elm/random
% elm install elm/json
% elm install elm/svg
% elm make MouseGames.elm --output=MouseGames.js
Then open MouseGames.html
to run the game.
Edit MouseGames.html
to pass the window size as flags from JavaScript to Elm during initialization.
(The rest of the edits, below, will be in MouseGames.elm
. Remember to recompile to MouseGames.js
every time.)
Subscribe to Browser.Events.onResize
to create Resize
messages, and handle them in update
.
Subscribe to Browser.Events.onMouseDown
to create MouseDown
messages. For now, have these messages carry dummy Point
values; we will worry about the actual click locations later.
Define generatorPoint
to generate random Point
s within the bounds of the current window. These random points will be used as the click targets in the game. (Hint: Random.map
*)
In init
, generate a random point.
In update
, generate a random point every time the mouse is clicked.
When a random point is generated, store it in the Model
as the nextTarget
.
In view
, use the elm/svg
package to draw the target, a red circle
.
Go back to the onMouseDown
subscription where MouseDown
messages are created, and let's actually track the locations of clicks. Decode the "clientX"
and "clientY"
fields from the browser JSON event value. (Hint: Decode.map
* and Decode.field
)
In update
for MouseDown
, calculate the distance from this click to the previous target, and store this distance in the Model
in lastClick
. (This field also stores the location of the click, though our view
function won't do anything with it.)
In view
, display the distance as a string.
Use the elm-ui
package to define an Input.button
for toggling whether or not to show the center of the target (see showCenter
and ToggleShowCenter
). Fill in maybeWhiteCircle
if you haven't already.
Define an Input.slider
for adjusting the radius of the target (see targetRadius
and SetTargetRadius
). Draw the redCircle
accordingly.