What is the best way to handle imperative elements (canvas, webgl, etc) in ocaml-vdom?
I can create a canvas element with
Vdom.elt in my
view function, but that’s a bit useless as I can’t figure out a sane way to get the context and draw to it.
Other virtual dom implementations have a hooks mechanism, or similar, that allow executing a function on the dom element after vdom->dom transition, but ocaml-vdom doesn’t seem to have this. I believe the Custom.t element is probably the intended way to handle this, but I can’t figure out how to use it. Where does the
Js_browser.Element.t come from in
Custom elements should be used. You need to extend
Vdom.Custom.t with a constructor holding the internal state of the imperative component, and then create a
Vdom_blit.Custom.handler that detects this constructor and produce a
Vdom_blit.Custom.t object. To do so, the handler needs to create the DOM element (such as a canvas, using
Js_browser) and pass it to
Vdom_blit.Custom.make, together with a
sync function. The handler can directly “talk” to the DOM element since it created it (in particular, it can use arguments of the
Vdom.Custom.t constructor to initialize, e.g. draw an initial shape to the canvas). And the
sync function is then in charge of talking again to the same DOM element (captured in its closure) to apply later changes to the state (stored in the constructor).
Does that make sense? If not, can you give a concrete example of a small application “workflow” that you would like to implement?
Thanks, I think I get it now. This is what I came up with as a simple trial:
type Custom.t += Canvas of string
let canvas_handler _ctx c =
match c with
| Canvas _ ->
let e = Document.create_element document "canvas" in
let sync = function
| Canvas color ->
begin match Canvas.get_context e with
| Some context ->
Canvas.set_fill_style context (`Color color);
Canvas.fill_rect context 0. 0. 100. 100.;
| None -> false
| _ -> false
ignore (sync c);
Some (Vdom_blit.Custom.make ~sync e)
| _ -> None
let canvas ?key ?a color = custom ?key ?a (Canvas color)
let () = Vdom_blit.(register (custom canvas_handler))
That’s about right. One thing: for performance reasons, you should keep track of the current color, and only send instructions to Canvas when it changes. Basically, for a Custom element, you need to implement the state diffing/DOM patching logic yourself.