elm christmas

SVG in Elm

←Previous postNext post →

A short introduction to playing with SVG in Elm.

A 5 min read written by
Fredrik Meyer

If you know how to write SVG, writing it in Elm is no different. You just type

elm install elm/svg

to install the SVG module. The syntax is a direct translation of regular SVG syntax. Whereas in regular SVG, you would write something like

<circle cx="50" cy="50" r="6" fill="yellow"/>

to draw a yellow circle at position (50, 50), in Elm you would write:

Svg.circle
    [ cx "50"
    , cy "50"
    , r "6"
    , fill "yellow"
    ]
    []

The type signature is List (Attribute msg) -> List (Svg msg) -> Svg msg. So to get an SVG element, we pass in first a list of attributes (things like position, color, etc.) a list of child elements (which in this case is empty).

So how would one go about drawing for example a... christmas three?

The base of the tree

Let us start with drawing the base:

width =
    "300"


height =
    "300"


main : Html.Html msg
main =
    svg
        [ Svg.Attributes.width width
        , Svg.Attributes.height height
        , Svg.Attributes.style "border: 1px solid black"
        , viewBox <| "0 0 " ++ width ++ " " ++ height
        ]
        [ Svg.defs []
            [ Svg.linearGradient [ Svg.Attributes.id "base" ]
                [ Svg.stop [ offset "0%", stopColor "brown" ] []
                , Svg.stop [ offset "100%" ] []
                ]
            ]

        -- BASE
        , Svg.polygon
            [ points <|
                polyPoints
                    [ ( 130, 250 )
                    , ( 170, 250 )
                    , ( 170, 300 )
                    , ( 130, 300 )
                    ]
            , fill "url(#base)"
            , stroke "black"
            ]
            []
        ]

This is maybe a lot at once: we first define the width and height of our viewbox, the "canvas" to put our figures in. Then we draw a rectangle placed in the middle bottom of the picture.

We fill the rectangle with a nice gradient, going from brown to black. The result looks like this:

Only the base.

Greanery

A good start! Let's add some greanery! A nice way to get a simple tree-ish feeling to the picture, is to stack a few green parallelogram on top of each other, and finish off with a triangle at the top. We do this with the following code:

        -- GREANERY
        , Svg.polygon
            [ points <|
                polyPoints
                    [ ( 50, 270 )
                    , ( 250, 270 )
                    , ( 200, 200 )
                    , ( 100, 200 )
                    ]
            , fill "url(#mygrad)"
            , stroke "black"
            ]
            []
        , Svg.polygon
            [ points <|
                polyPoints
                    [ ( 70, 230 )
                    , ( 230, 230 )
                    , ( 180, 150 )
                    , ( 120, 150 )
                    ]
            , fill "url(#mygrad)"
            , stroke "black"
            ]
            []
        , Svg.polygon
            [ points <|
                polyPoints
                    [ ( 90, 180 )
                    , ( 210, 180 )
                    , ( 150, 100 )
                    ]
            , fill "url(#mygrad)"
            , stroke "black"
            ]
            []

The fill attribute refers to the following, which is put inside a _SVG definitions node_, in Elm written Svg.defs [] [...], with the definitions inside the dotted bracket.

                [ Svg.Attributes.id "mygrad"
                , x1 "0"
                , x2 "0"
                , y1 "0"
                , y2 "1"
                ]
                [ Svg.stop [ offset "0%", stopColor "green" ] []
                , Svg.stop [ offset "100%" ] []
                ]

The attributes x1, x2, y1 and y2 define the orientation of gradient. The points (x1,y1) and (x2,y2) define a vertical line along which the gradient changes. I have also written a helper function polyPoints which takes a list of tuples and turn them into string arguments that fit the SVG schema. This is how our tree looks now: With greanery.

Glowing lights

Looks like a tree at least! Let's add some Christmas spirit by adding a few glowing lights. To get a glowy effect for the lights, we use two SVG circles: one for the light itself, and one for the glow. The following function takes two coordinates and produces the correct SVG element.

light : Int -> Int -> Svg.Svg msg
light xx yy =
    Svg.g []
        [ Svg.circle
            [ cx <| String.fromInt xx
            , cy <| String.fromInt yy
            , r "6"
            , Svg.Attributes.filter "url(#glow)"
            , fill "yellow"
            ]
            []
        , Svg.circle
            [ cx <| String.fromInt xx
            , cy <| String.fromInt yy
            , r "3"
            , fill "yellow"
            ]
            []
        ]

Apply this function a few times to fill the tree with lights. We should also add a star! I chose to just use a simple polygon to keep the simplistic style.

Finally we end up with something looking like this:

A christmas three.

It is admittedly not the most beautiful tree ever painted, but I think it gives us some ideas on what one can do with SVG.

Reading about SVG is fun, and I recommend reading the MDN web docs. The tutorials are very good and friendly.

The source code for this tree can be found here.

←Previous postNext post →