diff --git a/Client.fs b/Client.fs index 61d932d..a84ab0b 100644 --- a/Client.fs +++ b/Client.fs @@ -1,17 +1,23 @@ namespace web_api_cookbook open WebSharper -open WebSharper.JavaScript open WebSharper.UI open WebSharper.UI.Client open WebSharper.UI.Html -open WebSharper.UI.Templating +open Units.Animation +open Units.Time + [] module Client = [] let Main () = + let isClicked = Var.Create false + let opacityAnimated = Animate.valueWhen isClicked.View 1.0 0. 120. 1. + let opacityStyle = View.MapCached (sprintf "opacity: %f") opacityAnimated + let onClick = on.click (fun _ _ -> Var.Set isClicked true) + div [] [ - p [] [ text "hello world!" ] + UI.Components.button [attr.styleDyn opacityStyle; onClick] "Hide Me" ] |> Doc.RunById "main" diff --git a/Prelude.fs b/Prelude.fs new file mode 100644 index 0000000..e6b8629 --- /dev/null +++ b/Prelude.fs @@ -0,0 +1,31 @@ +namespace web_api_cookbook + +open WebSharper + +[] +module Option = + let getOrElse def opt = + if Option.isSome opt + then Option.get opt + else def + +[] +module Units = + module Animation = + [] + type frames + module Time = + [] + type ms + [] + type s + +[] +module Math = + let clamp a b c = + if c < a then a + else if c > b then b + else c + + [] + let inline lerp a b t = a + t * (b - a) diff --git a/Ui.fs b/Ui.fs new file mode 100644 index 0000000..d2d5f90 --- /dev/null +++ b/Ui.fs @@ -0,0 +1,52 @@ +namespace web_api_cookbook + +open WebSharper +open WebSharper.JavaScript +open WebSharper.UI +open WebSharper.UI.Client +open WebSharper.UI.Html + +[] +module UI = + module Components = + let button attrs label = + button (attrs @ [attr.``type`` "button"]) [ text label ] + +[] +module Animate = + open Units.Animation + open Units.Time + let value (startPoint:float) endPoint (targetFps:float) (animationSeconds:float) = + let frameInterval = 1. / targetFps * 1000.0 + let frameCount = animationSeconds * targetFps + let diff = startPoint - endPoint + let increment = abs (diff / frameCount) + let lerp = Math.lerp startPoint endPoint + let interpolatedValue = Var.Create startPoint + + let rec callback (lastRedrawAt:float option) frameNo (now:float) = + let now = now * 1.0 + let lastRedrawAt = Option.getOrElse now lastRedrawAt + let elapsed = now - lastRedrawAt + let readyToRender = elapsed >= frameInterval * 1. + match readyToRender with + | false -> + JS.Window.RequestAnimationFrame (callback (Some lastRedrawAt) frameNo) |> ignore + | true -> + let t = float frameNo * increment * 1. + let nextVal = lerp t + Var.Set interpolatedValue nextVal + if nextVal <> endPoint + then + JS.Window.RequestAnimationFrame (callback (Some now) (frameNo + 1)) + |> ignore + + JS.Window.RequestAnimationFrame (callback None 0) |> ignore + interpolatedValue.View + + let valueWhen beginAnimation startPoint endPoint targetFps animationSeconds = + function + | true -> value startPoint endPoint targetFps animationSeconds + | false -> View.Const startPoint + |> View.MapCached <| beginAnimation + |> View.Join diff --git a/web_api_cookbook.fsproj b/web_api_cookbook.fsproj index e297297..fc7d155 100644 --- a/web_api_cookbook.fsproj +++ b/web_api_cookbook.fsproj @@ -9,6 +9,8 @@ + +