Last active
August 16, 2021 09:25
-
-
Save martimatix/53aa808c6dd82ecbb51f27f5afc64e02 to your computer and use it in GitHub Desktop.
Elm Subscription Example - Pause and Resume the clock
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Html exposing (Html, p, label, br, input) | |
import Html.App as Html | |
import Html.Attributes exposing (checked) | |
import Html.Events exposing (onCheck) | |
import Svg exposing (..) | |
import Svg.Attributes exposing (..) | |
import Time exposing (Time, second) | |
main = | |
Html.program | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
-- MODEL | |
type alias Model = | |
{ time : Time | |
, sub : Bool | |
} | |
init : (Model, Cmd Msg) | |
init = | |
(Model 0 True, Cmd.none) | |
-- UPDATE | |
type Msg | |
= Tick Time | |
| ClockSub Bool | |
update : Msg -> Model -> (Model, Cmd Msg) | |
update msg model = | |
case msg of | |
Tick newTime -> | |
({ model | time = newTime }, Cmd.none) | |
ClockSub state -> | |
{ model | sub = state } ! [] | |
-- SUBSCRIPTIONS | |
subscriptions : Model -> Sub Msg | |
subscriptions model = | |
if model.sub then | |
Time.every second Tick | |
else | |
Sub.none | |
-- VIEW | |
view : Model -> Html Msg | |
view model = | |
Html.div [] | |
[ Html.p [] [ text "Turn subscription on or off" ] | |
, radio True "on" model | |
, radio False "off" model | |
, br [] [] | |
, clock model | |
] | |
radio : Bool -> String -> Model -> Html Msg | |
radio state name model = | |
let | |
isSelected = | |
model.sub == state | |
in | |
label [] | |
[ br [] [] | |
, input [ type' "radio", checked isSelected, onCheck (\_ -> ClockSub state) ] [] | |
, text name | |
] | |
clock : Model -> Html msg | |
clock model = | |
let | |
angle = | |
turns (Time.inMinutes model.time) | |
handX = | |
toString (50 + 40 * cos angle) | |
handY = | |
toString (50 + 40 * sin angle) | |
in | |
svg [ viewBox "0 0 100 100", width "300px" ] | |
[ circle [ cx "50", cy "50", r "45", fill "#0B79CE" ] [] | |
, line [ x1 "50", y1 "50", x2 handX, y2 handY, stroke "#023963" ] [] | |
] |
The turn
call is really cool ... the only missing part was to calculate the turn correctly dividing seconds to 60 in one turn, or 60 for minutes or 12 for hours in 12 hours format.
Here a version with seconds (I update it to Elm 0.19 that is the one I have now) ->
module Main exposing (main)
import Browser
import Html exposing (Html, br, input, label, p, text)
import Html.Attributes exposing (checked, type_)
import Html.Events exposing (onCheck)
import Svg exposing (circle, line, svg)
import Svg.Attributes exposing (cx, cy, fill, r, stroke, viewBox, width, x1, x2, y1, y2)
import Time exposing (Posix, utc)
main : Program () Model Msg
main =
Browser.element
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Model =
{ time : Posix
, sub : Bool
}
init : () -> ( Model, Cmd Msg )
init _ =
( Model (Time.millisToPosix 0) True, Cmd.none )
-- UPDATE
type Msg
= Tick Posix
| ClockSub Bool
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Tick newTime ->
( { model | time = newTime }, Cmd.none )
ClockSub state ->
( { model | sub = state }, Cmd.none )
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
if model.sub then
Time.every 500 Tick
else
Sub.none
-- VIEW
view : Model -> Html Msg
view model =
Html.div []
[ p [] [ text "Turn subscription on or off" ]
, radio True "on" model
, radio False "off" model
, br [] []
, clock model
]
radio : Bool -> String -> Model -> Html Msg
radio state name model =
let
isSelected =
model.sub == state
in
label []
[ br [] []
, input [ type_ "radio", checked isSelected, onCheck (\_ -> ClockSub state) ] []
, text name
]
clock : Model -> Html msg
clock model =
let
angle =
turns (toFloat (Time.toSecond utc model.time) / 60.0)
handX =
String.fromFloat (50 + 40 * cos angle)
handY =
String.fromFloat (50 + 40 * sin angle)
in
svg [ viewBox "0 0 100 100", width "300px" ]
[ circle [ cx "50", cy "50", r "45", fill "#0B79CE" ] []
, line [ x1 "50", y1 "50", x2 handX, y2 handY, stroke "#023963" ] []
]
The solution of Roman is also cool and more complete, but it can be simplified a little bit if turns
is used.
Thanks for sharing guys !! ... Elm is great !! 👍 😎
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The
Time.every second
call is NOT precise, it's just a suggestion to try to run every second ... meaning -> sometimes will run at the precise second but other times, if the system is busy it will run at slightly more than a second ... if you add always one second, the clock will be unsync more and more every tick ... it's more precise to use newTime.