SvgClock
SvgClock.ml
module P = Preact
module S = P.Svg
module Clock = struct
let utcNow () =
Js.Date.now () -. (Js.Date.getTimezoneOffset (Js.Date.make ()) *. 60.0 *. 1000.0)
let make =
fun [@preact.component "Clock"] () ->
let[@hook] isRunning, setIsRunning = P.useState true in
let[@hook] now, setNow = P.useState (Js.Date.now ()) in
let[@hook] () = AnimationFrame.use (isRunning, fun _ -> setNow (utcNow ())) in
let circle =
S.circle [ S.cx "100"; S.cy "100"; S.r "98"; S.fill "none"; S.stroke "black" ] []
in
let line rotate stroke strokeWidth height =
S.line
[ S.x1 "100"
; S.y1 "100"
; S.x2 (Js.Int.toString (100 - height))
; S.y2 "100"
; S.stroke stroke
; S.strokeWidth (Js.Int.toString strokeWidth)
; S.strokeLinecap "round"
; S.transform ("rotate(" ^ Js.Float.toString rotate ^ " 100 100)")
]
[]
in
let s = now /. 1000.0 in
let secondRotate = 90.0 +. (mod_float s 60.0 *. 360.0 /. 60.0) in
let minuteRotate = 90.0 +. (mod_float (s /. 60.0) 60.0 *. 360.0 /. 60.0) in
let hourRotate = 90.0 +. (mod_float (s /. 60.0 /. 60.0) 12.0 *. 360.0 /. 12.0) in
P.h2
[ P.style "text-align" "center" ]
[ S.svg
[ S.width "400"; S.height "400"; S.viewBox "0 0 200 200" ]
[ circle
; line hourRotate "#333" 4 50
; line minuteRotate "#333" 3 70
; line secondRotate "crimson" 2 90
]
; P.div
[]
[ P.button
[ P.onClick (fun _ -> setIsRunning (not isRunning)) ]
[ P.string (if isRunning then "Stop" else "Start") ]
]
]
end
let () =
match P.find "main" with
| Some element -> P.render (Clock.make ()) element
| None -> Js.Console.error "<main> not found!"
AnimationFrame.ml
module P = Preact
let use =
fun [@preact.hook] (enabled, func) ->
let[@hook] () =
P.useEffect
(fun () ->
let id = ref None in
let rec loop t =
let () = func t in
let () = id := Some (Webapi.requestCancellableAnimationFrame loop) in
()
in
let () =
if enabled
then id := Some (Webapi.requestCancellableAnimationFrame loop)
else ()
in
Some
(fun () ->
match !id with
| Some id -> Webapi.cancelAnimationFrame id
| None -> ()))
(P._1 enabled)
in
()