shamansir / elm-canvas / Canvas

This module exposes a nice drawing API that works on top of the the DOM canvas.

See instructions in the main page of the package for installation, as it requires the elm-canvas web component to work.

Usage in HTML

toHtml : ( Basics.Int, Basics.Int ) -> List (Html.Attribute msg) -> List Renderable -> Html msg

Create a Html element that you can use in your view.

Canvas.toHtml ( width, height )
    [ style "display" "block", onClick CanvasClick ]
    [ shapes [ fill Color.white ] [ rect ( 0, 0 ) w h ]
    , text
        [ font { size = 48, family = "sans-serif" }, align Center ]
        ( 50, 50 )
        "Hello world"
    ]

toHtml is almost like creating other Html elements. We need to pass (width, height) in pixels, a list of Html.Attribute, and finally instead of a list of html elements, we pass a List Renderable. A Renderable is a thing that the canvas knows how to render. Read on for more information 👇.

Note: Remember to include the elm-canvas web component from npm in your page for this to work!

Note: This element has display: inline by default, so their width or height will have no effect. You can change it to block for example. See MDN: display for possible display values.

toHtmlWith : { width : Basics.Int, height : Basics.Int, textures : List (Texture.Source msg) } -> List (Html.Attribute msg) -> List Renderable -> Html msg

Similar to toHtml but with more explicit options and the ability to load textures.

Canvas.toHtmlWith
    { width = 500
    , height = 500
    , textures = [ Texture.loadImageUrl "./assets/sprite.png" TextureLoaded ]
    }
    [ style "display" "block", onClick CanvasClick ]
    [ shapes [ fill Color.white ] [ rect ( 0, 0 ) w h ]
    , text
        [ font { size = 48, family = "sans-serif" }, align Center ]
        ( 50, 50 )
        "Hello world"
    ]

Note: Remember to include the elm-canvas web component from npm in your page for this to work!

Note: This element has display: inline by default, so their width or height will have no effect. You can change it to block for example. See MDN: display for possible display values.

See toHtml above and the Canvas.Texture module for more details.

Drawing things


type alias Renderable =
Internal.Renderable

A Renderable is a thing that the canvas knows how to render, similar to Html elements.

We can make Renderables to use with Canvas.toHtml with functions like shapes and text.


type alias Point =
( Basics.Float, Basics.Float )

A small alias to reference points on some of the functions on the package.

The first argument of the tuple is the x position, and the second is the y position.

-- Making a point with x = 15 and y = 55
point : Point
point =
    ( 15, 55 )

clear : Point -> Basics.Float -> Basics.Float -> Renderable

We use clear to remove the contents of a rectangle in the screen and make them transparent.

import Canvas exposing (..)

Canvas.toHtml ( width, height )
    []
    [ clear ( 0, 0 ) width height
    , shapes [ fill Color.red ] [ rect ( 10, 10 ) 20 20 ]
    ]

shapes : List Internal.Setting -> List Shape -> Renderable

We use shapes to render different shapes like rectangles, circles, and lines of different kinds that we can connect together.

You can draw many shapes with the same Settings, which makes for very efficient rendering.

import Canvas exposing (..)
import Color -- elm install avh4/elm-color

Canvas.toHtml ( width, height )
    []
    [ shapes [ fill Color.white ] [ rect ( 0, 0 ) width height ] ]

You can read more about the different kinds of Shape in the Drawing shapes section.

text : List Internal.Setting -> Point -> String -> Renderable

We use text to render text on the canvas. We need to pass the list of settings to style it, the point with the coordinates where we want to render, and the text to render.

Keep in mind that align and other settings can change where the text is positioned with regards to the coordinates provided.

Canvas.toHtml ( width, height )
    []
    [ text
        [ font { size = 48, family = "sans-serif" }, align Center ]
        ( 50, 50 )
        "Hello world"
    ]

You can learn more about drawing text and its settings in the Drawing text section.

texture : List Internal.Setting -> Point -> Texture -> Renderable

Draw a texture into your canvas.

Textures can be loaded by using toHtmlWith and passing in a Texture.Source. Once the texture is loaded, and you have an actual Texture, you can use it with this method to draw it.

You can also make different types of textures from the same texture, in case you have a big sprite sheet and want to create smaller textures that are a viewport into a bigger sheet.

See the Canvas.Texture module and the sprite function in it.

group : List Internal.Setting -> List Renderable -> Renderable

Groups many renderables into one, and provides the opportunity to apply settings for the whole group.

Canvas.toHtml ( width, height )
    []
    [ group [ fill Color.red ]
        [ shapes [] [ rect ( 0, 0 ) w h ]
        , text
            [ font { size = 48, family = "sans-serif" }, align Center ]
            ( 50, 50 )
            "Hello world"
        ]
    ]

Drawing shapes

Shapes can be rectangles, circles, and different types of lines. By composing shapes, you can draw complex figures! There are many functions that produce a Shape, which you can feed to shapes to get something on the screen.


type alias Shape =
Internal.Shape

A Shape represents a shape or lines to be drawn. Giving them to shapes we get a Renderable for the canvas.

shapes []
    [ path ( 20, 10 )
        [ lineTo ( 10, 30 )
        , lineTo ( 30, 30 )
        , lineTo ( 20, 10 )
        ]
    , circle ( 50, 50 ) 10
    , rect ( 100, 150 ) 40 50
    , circle ( 100, 100 ) 80
    ]

rect : Point -> Basics.Float -> Basics.Float -> Shape

Creates the shape of a rectangle. It needs the position of the top left corner, the width, and the height.

rect pos width height

roundRect : Point -> Basics.Float -> Basics.Float -> List Basics.Float -> Shape

Creates the shape of a rounded rectangle. It takes the position of the top left corner, the width, the height and a list specifying the radii of the circular arc to be used for the corners of the rectangle. The list must contain between 1 and 4 positive numbers. You can find more info on this page.

circle : Point -> Basics.Float -> Shape

Creates a circle. It takes the position of the center of the circle, and the radius of it.

circle pos radius

arc : Point -> Basics.Float -> { startAngle : Basics.Float, endAngle : Basics.Float, clockwise : Basics.Bool } -> Shape

Creates an arc, a partial circle. It takes:

Note: If you want to give the angles in degrees, you can use the degrees function from elm/core.

arc ( 10, 10 ) 40 { startAngle = degrees 15, endAngle = degrees 85, clockwise = True }

Note: If you want to make a partial circle (like a pizza slice), combine with path to make a triangle, and then the arc. See the pie chart example.

path : Point -> List PathSegment -> Shape

Creates a complex path as a shape from a list of PathSegment instructions.

It is mandatory to pass in the starting point for the path, since the path starts with an implicit moveTo the starting point to avoid undesirable behavior and implicit state.

path startingPoint segments

Paths

In order to make a complex path, we need to put together a list of PathSegment


type alias PathSegment =
Internal.PathSegment

In order to draw a path, you need to give the function path a list of PathSegment

arcTo : Point -> Point -> Basics.Float -> PathSegment

Adds an arc to the path with the given control points and radius.

The arc drawn will be a part of a circle, never elliptical. Typical use could be making a rounded corner.

One way to think about the arc drawn is to imagine two straight segments, from the starting point (latest point in current path) to the first control point, and then from the first control point to the second control point. These two segments form a sharp corner with the first control point being in the corner. Using arcTo, the corner will instead be an arc with the given radius.

The arc is tangential to both segments, which can sometimes produce surprising results, e.g. if the radius given is larger than the distance between the starting point and the first control point.

If the radius specified doesn't make the arc meet the starting point (latest point in the current path), the starting point is connected to the arc with a straight line segment.

arcTo ( x1, y1 ) ( x2, y2 ) radius

You can see more examples and docs in this page

bezierCurveTo : Point -> Point -> Point -> PathSegment

Adds a cubic Bézier curve to the path. It requires three points. The first two points are control points and the third one is the end point. The starting point is the last point in the current path, which can be changed using moveTo before creating the Bézier curve. You can learn more about this curve in the MDN docs.

bezierCurveTo controlPoint1 controlPoint2 point

bezierCurveTo ( cp1x, cp1y ) ( cp2x, cp2y ) ( x, y )

lineTo : Point -> PathSegment

Connects the last point in the previous shape to the x, y coordinates with a straight line.

lineTo ( x, y )

If you want to make a line independently of where the previous shape ended, you can use moveTo before using lineTo.

moveTo : Point -> PathSegment

moveTo doesn't necessarily produce any shape, but it moves the starting point somewhere so that you can use this with other lines.

moveTo point

quadraticCurveTo : Point -> Point -> PathSegment

Adds a quadratic Bézier curve to the path. It requires two points. The first point is a control point and the second one is the end point. The starting point is the last point in the current path, which can be changed using moveTo before creating the quadratic Bézier curve. Learn more about quadratic bezier curves in the MDN docs

quadraticCurveTo controlPoint point

quadraticCurveTo ( cpx, cpy ) ( x, y )