Skip to content

Instantly share code, notes, and snippets.

@wat-aro
Created September 28, 2018 05:02
Show Gist options
  • Save wat-aro/c8e8f58effb66028815e1176aa02cc3c to your computer and use it in GitHub Desktop.
Save wat-aro/c8e8f58effb66028815e1176aa02cc3c to your computer and use it in GitHub Desktop.
SVG Clock
module Main exposing (Model, init, main)
import Browser
import Html exposing (..)
import Html.Attributes as Attributes
import Html.Events exposing (onClick)
import Svg
import Svg.Attributes
import Task
import Time
main =
Browser.element
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
type TimeStreaming
= Streaming
| Stop
type alias Model =
{ zone : Time.Zone
, time : Time.Posix
, timeStreaming : TimeStreaming
}
init : () -> ( Model, Cmd Msg )
init _ =
( Model Time.utc (Time.millisToPosix 0) Streaming
, Task.perform AdjustTimeZone Time.here
)
type Msg
= Tick Time.Posix
| AdjustTimeZone Time.Zone
| StartStreaming
| StopStreaming
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Tick newTime ->
( { model | time = newTime }
, Cmd.none
)
AdjustTimeZone newZone ->
( { model | zone = newZone }
, Cmd.none
)
StopStreaming ->
( { model | timeStreaming = Stop }
, Cmd.none
)
StartStreaming ->
( { model | timeStreaming = Streaming }
, Cmd.none
)
subscriptions : Model -> Sub Msg
subscriptions model =
case model.timeStreaming of
Streaming ->
Time.every 1000 Tick
Stop ->
Sub.none
radius : Int
radius =
120
radiusString : String
radiusString =
String.fromInt radius
radiusFloat : Float
radiusFloat =
toFloat radius
diameterString : String
diameterString =
2 * radius |> String.fromInt
view : Model -> Html Msg
view model =
h1 []
[ Svg.svg
[ Svg.Attributes.width diameterString
, Svg.Attributes.height diameterString
, Svg.Attributes.viewBox <| "0 0 " ++ diameterString ++ " " ++ diameterString
]
[ outerCircle
, innerCircle
, hand model Hour
, hand model Minute
, hand model Second
]
, div [] [ streamingButton model ]
]
type HandType
= Hour
| Minute
| Second
strokeHand : HandType -> String
strokeHand handType =
case handType of
Hour ->
"3"
Minute ->
"3"
Second ->
"1"
handLength : HandType -> Float
handLength handType =
case handType of
Hour ->
35.0 * radiusFloat / 60.0
Minute ->
55.0 * radiusFloat / 60.0
Second ->
55.0 * radiusFloat / 60.0
handAngle : Model -> HandType -> Float
handAngle model handType =
case handType of
Hour ->
2 * pi * toFloat (modBy 12 (hour model)) / 12.0 + (toFloat (minute model) / 60.0 * pi / 6.0) - pi / 2
Minute ->
2 * pi * toFloat (minute model) / 60.0 - pi / 2
Second ->
2 * pi * toFloat (second model) / 60.0 - pi / 2
hour : Model -> Int
hour model =
Time.toHour model.zone model.time
minute : Model -> Int
minute model =
Time.toMinute model.zone model.time
second : Model -> Int
second model =
Time.toSecond model.zone model.time
outerCircle : Svg.Svg Msg
outerCircle =
Svg.circle
[ Svg.Attributes.cx radiusString
, Svg.Attributes.cy radiusString
, Svg.Attributes.r radiusString
, Svg.Attributes.fill "black"
]
[]
innerCircle : Svg.Svg Msg
innerCircle =
Svg.circle
[ Svg.Attributes.cx radiusString
, Svg.Attributes.cy radiusString
, radius - 1 |> String.fromInt |> Svg.Attributes.r
, Svg.Attributes.fill "white"
]
[]
hand : Model -> HandType -> Svg.Svg Msg
hand model handType =
Svg.line
[ Svg.Attributes.x1 radiusString
, Svg.Attributes.y1 radiusString
, cos (handAngle model handType) * handLength handType + radiusFloat |> String.fromFloat |> Svg.Attributes.x2
, sin (handAngle model handType) * handLength handType + radiusFloat |> String.fromFloat |> Svg.Attributes.y2
, strokeHand handType |> Svg.Attributes.strokeWidth
, Svg.Attributes.stroke "black"
]
[]
streamingButton : Model -> Html Msg
streamingButton { timeStreaming } =
case timeStreaming of
Streaming ->
button [ onClick StopStreaming ] [ text "Stop" ]
Stop ->
button [ onClick StartStreaming ] [ text "Start" ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment