last month I helped a friend work through creating an svg element with reflexfrp. it would not show up in the browser so have been trying to figure out how to get this working.
First tried compiling ghcjs svg.hs
(below) with the following contents.
ghcjs svg.hs
-- svg.hs
import reflex.dom
import qualified data.map as map
main = mainwidget mydiv
mydiv = el "svg" do
elattr "circle" (map.fromlist [ ("cx" , "50") , ("cy", "50"), ("r" , "40"), ("stroke" , "green"), ("fill", "yellow" )]) return ()
it compiles into a working webpage, however nothing shows up in the browser! whaaaaat??? time to delve into the svg docs from w3 to see what might be required when defining an svg tag properly
an svg document fragment can range from an empty fragment (i.e., no content inside of the ‘svg’ element), to a very simple svg document fragment containing a single svg graphics element such as a ‘rect’, to a complex, deeply nested collection of container elements and graphics elements. - w3.org
As I read further, they tell me my svg elements should look like this:
<svg xmlns="http://www.w3.org/2000/svg" …>
<rect …/>
</svg>
edited svg.hs
and tried again, using elattr
instead of el
to add attributes to the svg element but guess what, this also didn’t show anything in the browser.
-- don't do this, it won't work
elattr "svg"
("xmlns" =: ns
<> "width" =: "200.0"
<> "height" =: "200.0"
<> "viewbox" =: "0 0 50 20"
<> "xmlns:xlink" =: "http://www.w3.org/1999/xlink"
<> "version" =: "1.0"
<> "xmlns:svg" =: "http://www.w3.org/2000/svg"
)
Next, looked for working examples to see how others tackle this problem. In the course of this I found two places that posted examples.
Diagrams-Reflex
There are working examples of using the graphics diagrams library to render svg elements with reflex in the diagrams-reflex repo.
At this time there are three known examples with this repo: (1) Click counting, source; (2) Mouse position, source; and (3) Colors, source
No documentation found.
Rotating Cube
On another site, an example of a rotating cube. Note, to work with the try-reflex default install you’d need to add the matrix library to list of packages.
This was the one that made me see that both the "svg"
and "polygon"
tags call this one function that uses elDynAttrNS'
:
elDynAttrSVG a2 a3 a4 = do
elDynAttrNS' (Just "http://www.w3.org/2000/svg") a2 a3 a4
return ()
Bingo.
Summary
What I learned - when working with the xml namespace, there’s a specific function available in reflex-dom and this can also be used to produce dynamic svg elements. Is the dynamic version better than a static elAttr
? No clue, but using elDynAttrNS'
works and I’ll be scaling towards creating dynamic graphics in the browser I’ll probably stick to picking that option.
What is elDynAttrNS’?
From the Hackage docs we can read the type signature.
elDynAttrNS' :: forall t m a. MonadWidget t m => Maybe String -> String -> Dynamic t (Map String String) -> m a -> m (El t, a)
What this means: four inputs, the first is the xml namespace declaration, the 2nd the svg element, the third includes further attributes, and the last is a monad. This returns a monad, so you have to bind it to something~
example <- elDynAttrNS' (Just "http://www.w3.org/2000/svg") "svg" attributes mAttr
-- don't do this
elAttr "svg" ("xmlns" =: ns <> "width" =: "200.0" <> "height" =: "200.0"
<> "viewBox" =: "0 0 50 20"
<> "xmlns:xlink" =: "http://www.w3.org/1999/xlink"
<> "version" =: "1.0" <> "xmlns:svg" =: "http://www.w3.org/2000/svg"
) do
-- do this instead
s <- elDynAttrNS' ns "svg" attrs (elDynAttrNS' ns "circle" cAttrs return ())
Ahh success. Feels good to have figured out a working solution. I’ll update the sample svg.hs
file with the working code
Links