1effect module Browser.AnimationManager where { subscription = MySub } exposing
2  ( onAnimationFrame
3  , onAnimationFrameDelta
4  )
5
6
7import Elm.Kernel.Browser
8import Process
9import Task exposing (Task)
10import Time
11
12
13
14-- PUBLIC STUFF
15
16
17onAnimationFrame : (Time.Posix -> msg) -> Sub msg
18onAnimationFrame tagger =
19  subscription (Time tagger)
20
21
22onAnimationFrameDelta : (Float -> msg) -> Sub msg
23onAnimationFrameDelta tagger =
24  subscription (Delta tagger)
25
26
27
28-- SUBSCRIPTIONS
29
30
31type MySub msg
32  = Time (Time.Posix -> msg)
33  | Delta (Float -> msg)
34
35
36subMap : (a -> b) -> MySub a -> MySub b
37subMap func sub =
38  case sub of
39    Time tagger ->
40      Time (func << tagger)
41
42    Delta tagger ->
43      Delta (func << tagger)
44
45
46
47-- EFFECT MANAGER
48
49
50type alias State msg =
51  { subs : List (MySub msg)
52  , request : Maybe Process.Id
53  , oldTime : Int
54  }
55
56
57-- NOTE: used in onEffects
58--
59init : Task Never (State msg)
60init =
61  Task.succeed (State [] Nothing 0)
62
63
64onEffects : Platform.Router msg Int -> List (MySub msg) -> State msg -> Task Never (State msg)
65onEffects router subs {request, oldTime} =
66  case (request, subs) of
67    (Nothing, []) ->
68      init
69
70    (Just pid, []) ->
71      Process.kill pid
72        |> Task.andThen (\_ -> init)
73
74    (Nothing, _) ->
75      Process.spawn (Task.andThen (Platform.sendToSelf router) rAF)
76        |> Task.andThen (\pid -> now
77        |> Task.andThen (\time -> Task.succeed (State subs (Just pid) time)))
78
79    (Just _, _) ->
80      Task.succeed (State subs request oldTime)
81
82
83onSelfMsg : Platform.Router msg Int -> Int -> State msg -> Task Never (State msg)
84onSelfMsg router newTime {subs, oldTime} =
85  let
86    send sub =
87      case sub of
88        Time tagger ->
89          Platform.sendToApp router (tagger (Time.millisToPosix newTime))
90
91        Delta tagger ->
92          Platform.sendToApp router (tagger (toFloat (newTime - oldTime)))
93  in
94  Process.spawn (Task.andThen (Platform.sendToSelf router) rAF)
95    |> Task.andThen (\pid -> Task.sequence (List.map send subs)
96    |> Task.andThen (\_ -> Task.succeed (State subs (Just pid) newTime)))
97
98
99rAF : Task x Int
100rAF =
101  Elm.Kernel.Browser.rAF ()
102
103
104now : Task x Int
105now =
106  Elm.Kernel.Browser.now ()