Skip to content

Instantly share code, notes, and snippets.

@wat-aro
Created September 28, 2018 05:02
Show Gist options
  • Select an option

  • Save wat-aro/c8e8f58effb66028815e1176aa02cc3c to your computer and use it in GitHub Desktop.

Select an option

Save wat-aro/c8e8f58effb66028815e1176aa02cc3c to your computer and use it in GitHub Desktop.

Revisions

  1. wat-aro renamed this gist Sep 28, 2018. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. wat-aro created this gist Sep 28, 2018.
    222 changes: 222 additions & 0 deletions SvgClock.elm
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,222 @@
    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" ]