joakin/elm-canvas/3.0.0

NOTE: the latest version is 5.0.0

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 : ( Int, Int ) -> List (Attribute msg) -> List Renderable -> Html msg

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

Canvas.toHtml ( width, height )
    [ onClick CanvasClick ]
    [ shapes [ fill Color.white ] [ rect ( 0, 0 ) w h ]
    , text [ size 48, 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!

Drawing things

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 Setting

Similar to Html.Attribute, settings control the presentation and other style options for the Renderables.

shapes : List 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.

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 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 [ size 48, align Center ] ( 50, 50 ) "Hello world" ]

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

type alias Point =
( Float, 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 )

Styling the things you draw

The two main style settings are fill color and stroke color, which are documented here.

fill : Color -> Setting

By default, renderables are drawn with black color. If you want to specify a different color to draw, use this Setting on your renderable.

shapes
    [ fill Color.green ]
    [ rect ( 10, 30 ) 50 50 ]
stroke : Color -> Setting

By default, renderables are drawn with no visible stroke. If you want to specify a stroke color to draw an outline over your renderable, use this Setting on it.

shapes
    [ stroke Color.red ]
    [ rect ( 10, 30 ) 50 50 ]

There are other style settings in the documentation (if you search for things that return a Setting you can see). More specifically:

  • There are some style settings that only apply when drawing text, and you can find them in the Drawing text section.
  • There are other more advanced rendering settings that you can read about further down in the Advanced rendering settings section. They cover things like:
    • Line settings that apply to paths and shapes and text with stroke.
    • Shadows.
    • Matrix transforms.
    • And other more advanced topics like compositing mode.

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 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
    ]

Here are the different functions that produce shapes that we can draw.

rect : Point -> Float -> 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
circle : Point -> 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
-> Float
-> { startAngle : Float
, endAngle : Float
, clockwise : Bool
}
-> Shape

Creates an arc, a partial circle. It takes the position of the center of the circle, the radius of it, the start angle where the arc will start, the end angle where the arc will end, and if it should draw in clockwise or anti-clockwise direction.

arc ( 10, 10 ) 40 { startAngle = 15, endAngle = 85, clockwise = True }
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

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

arcTo : Point -> Point -> 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 )
  • cp1x
    • The x axis of the coordinate for the first control point.
  • cp1y
    • The y axis of the coordinate for the first control point.
  • cp2x
    • The x axis of the coordinate for the second control point.
  • cp2y
    • The y axis of the coordinate for the second control point.
  • x
    • The x axis of the coordinate for the end point.
  • y
    • The y axis of the coordinate for the end point.
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 )
  • cpx
    • The x axis of the coordinate for the control point.
  • cpy
    • The y axis of the coordinate for the control point.
  • x
    • The x axis of the coordinate for the end point.
  • y
    • The y axis of the coordinate for the end point.

Drawing text

To draw text we use the function text documented above:

text
    [ font { size = 48, family = "serif" }
    , align Center
    ]
    ( 50, 50 )
    "Hello world"

You can apply the following styling settings to text specifically. They will do nothing if you apply them to other renderables, like shapes.

{ size : Int
, family : String
}

Specify the font size and family to use when rendering text.

  • size: What is the size of the text in pixels. Similar to the font-size property in CSS.
  • family: Font family name to use when drawing the text. For example, you can use "monospace", "serif" or "sans-serif" to use the user configured default fonts in the browser. You can also specify other font names like "Consolas".

Specifies the text alignment to use when drawing text. Beware that the alignment is based on the x value of position passed to text. So if textAlign is Center, then the text would be drawn at x - (width / 2).

The default value is Start. MDN docs

= Left
| Right
| Center
| Start
| End

Type of text alignment

  • Left
    • The text is left-aligned.
  • Right
    • The text is right-aligned.
  • Center
    • The text is centered.
  • Start
    • The text is aligned at the normal start of the line (left-aligned for left-to-right locales, right-aligned for right-to-left locales).
  • End
    • The text is aligned at the normal end of the line (right-aligned for left-to-right locales, left-aligned for right-to-left locales).

Specifies the current text baseline being used when drawing text.

The default value is Alphabetic.

See MDN docs for examples and rendering of the different modes.

= Top
| Hanging
| Middle
| Alphabetic
| Ideographic
| Bottom

Type of text baseline.

  • Top
    • The text baseline is the top of the em square.
  • Hanging
    • The text baseline is the hanging baseline. (Used by Tibetan and other Indic scripts.)
  • Middle
    • The text baseline is the middle of the em square.
  • Alphabetic
    • The text baseline is the normal alphabetic baseline.
  • Ideographic
    • The text baseline is the ideographic baseline; this is the bottom of the body of the characters, if the main body of characters protrudes beneath the alphabetic baseline. (Used by Chinese, Japanese and Korean scripts.)
  • Bottom
    • The text baseline is the bottom of the bounding box. This differs from the ideographic baseline in that the ideographic baseline doesn't consider descenders.

Advanced rendering settings

The following are settings that you can apply, to create very specific and custom effects.

Line settings

Line style settings apply to paths, and the stroke of shapes and text (if any).

lineWidth : Float -> Setting

Specify the thickness of lines in space units. When passing zero, negative, Infinity and NaN values are ignored. More information and examples in the MDN docs

Determines how the end points of every line are drawn. See LineCap for the possible types. By default ButtCap is used. See the MDN docs for examples.

type LineCap
= ButtCap
| RoundCap
| SquareCap

Type of end points for line drawn.

  • ButtCap
    • The ends of lines are squared off at the endpoints.
  • RoundCap
    • The ends of lines are rounded.
  • SquareCap
    • The ends of lines are squared off by adding a box with an equal width and half the height of the line's thickness.

Specify how two connecting segments (of lines, arcs or curves) with non-zero lengths in a shape are joined together (degenerate segments with zero lengths, whose specified endpoints and control points are exactly at the same position, are skipped). See the type LineJoin.

By default this property is set to MiterJoin. Note that the lineJoin setting has no effect if the two connected segments have the same direction, because no joining area will be added in this case.

More information and examples in the MDN docs

= BevelJoin
| RoundJoin
| MiterJoin

Determines how two connecting segments with non-zero lengths in a shape are joined together.

  • Round
    • Rounds off the corners of a shape by filling an additional sector of disc centered at the common endpoint of connected segments. The radius for these rounded corners is equal to the line width.
  • Bevel
    • Fills an additional triangular area between the common endpoint of connected segments, and the separate outside rectangular corners of each segment.
  • Miter
    • Connected segments are joined by extending their outside edges to connect at a single point, with the effect of filling an additional lozenge-shaped area. This setting is affected by the miterLimit property.

You can see examples and pictures on the MDN docs

lineDash : List Float -> Setting

Specify the line dash pattern used when stroking lines, using a list of values which specify alternating lengths of lines and gaps which describe the pattern.

lineDash segments
  • segments
    • A list of numbers which specify distances to alternately draw a line and a gap (in coordinate space units). If the number of elements in the list is odd, the elements of the list get copied and concatenated. For example, [5, 15, 25] will become [5, 15, 25, 5, 15, 25]. If the list is empty, the line dash list is clear and line strokes are solid.

You can see examples and more information in the MDN docs

Specify the line dash pattern offset or "phase".

There are visual examples and more information in the MDN docs

miterLimit : Float -> Setting

Specify the miter limit ratio in space units. When passing zero, negative, Infinity and NaN values are ignored. It defaults to 10.

More information and live example in the MDN docs

Shadows

The shadow setting allows you to create a shadow for a renderable, similar to what the box-shadow CSS does to HTML elements.

shadow : Shadow -> Setting

Specify a shadow to be rendered. See the Shadow type alias to know what parameters to pass.

type alias Shadow =
{ blur : Float
, color : Color
, offset : ( Float, Float )
}

Record with the settings for a shadow.

  • blur: Amount of blur for the shadow
  • color: Color of the shadow
  • offset: ( xOffset, yOffset )

Transforms: scaling, rotating, translating, and matrix transformations

Transforms are very useful as they allow you to manipulate the rendering via a transformation matrix, allowing you to translate, scale, rotate and skew the rendering context easily. They can be a bit of an advanced topic, but they are powerful and can be very useful.

Specify the transform matrix to apply when drawing. You do so by applying transforms in order, like translate, rotate, or scale, but you can also use applyMatrix and set the matrix yourself manually if you know what you are doing.

shapes
    [ transform [ rotate (degrees 50) ] ]
    [ rect ( 40, 40 ) 20 20 ]

Type of transform to apply to the matrix, to be used in transform. See the functions below to learn how to create transforms.

translate : Float -> Float -> Transform

Adds a translation transformation by moving the canvas and its origin x units horizontally and y units vertically on the grid.

More information and examples on the MDN docs

translate x y
rotate : Float -> Transform

Adds a rotation to the transformation matrix. The angle argument represents a clockwise rotation angle and is expressed in radians. Use the core function degrees to make it easier to represent the rotation.

rotate (degrees 90)

The rotation center point is always the canvas origin. To change the center point, we will need to move the canvas by using the translate transform before rotating. For example, a very common use case to rotate from a specific point in the canvas, maybe the center of your renderable, would be done by translating to that point, rotating, and then translating back, if you want to apply more transformations. Like this:

transform
    [ translate x y
    , rotate rotation
    , translate -x -y


    {- Other transforms -}
    ]

See the MDN docs for more information and examples.

scale : Float -> Float -> Transform

Adds a scaling transformation to the canvas units by x horizontally and by y vertically.

By default, one unit on the canvas is exactly one pixel. If we apply, for instance, a scaling factor of 0.5, the resulting unit would become 0.5 pixels and so shapes would be drawn at half size. In a similar way setting the scaling factor to 2.0 would increase the unit size and one unit now becomes two pixels. This results in shapes being drawn twice as large.

scale x y
  • x
    • Scaling factor in the horizontal direction.
  • y
    • Scaling factor in the vertical direction.

Note: You can use scale -1 1 to flip the context horizontally and scale 1 -1 to flip it vertically.

More information and examples in the MDN docs

{ m11 : Float
, m12 : Float
, m21 : Float
, m22 : Float
, dx : Float
, dy : Float
}

Multiplies the current transformation with the matrix described by the arguments of this method. You are able to scale, rotate, move and skew the context.

  • m11
    • Horizontal scaling.
  • m12
    • Horizontal skewing.
  • m21
    • Vertical skewing.
  • m22
    • Vertical scaling.
  • dx
    • Horizontal moving.
  • dy
    • Vertical moving.

Alpha and global composite mode

Finally, there are a couple of other settings that you can use to create interesting visual effects:

alpha : Float -> Setting

Specifies the alpha value that is applied before renderables are drawn onto the canvas. The value is in the range from 0.0 (fully transparent) to 1.0 (fully opaque). The default value is 1.0. Values outside the range, including Infinity and NaN will not be set and alpha will remain default.

Specify the type of compositing operation to apply when drawing new entities, where type is a GlobalCompositeOperationMode identifying which of the compositing or blending mode operations to use.

See GlobalCompositeOperationMode below for more information.

compositeOperationMode Screen
= SourceOver
| SourceIn
| SourceOut
| SourceAtop
| DestinationOver
| DestinationIn
| DestinationOut
| DestinationAtop
| Lighter
| Copy
| Xor
| Multiply
| Screen
| Overlay
| Darken
| Lighten
| ColorDodge
| ColorBurn
| HardLight
| SoftLight
| Difference
| Exclusion
| Hue
| Saturation
| Color
| Luminosity

Type of compositing operation, identifying which of the compositing or blending mode operations to use. See the chapter Compositing from the Canvas Tutorial.

For more information and pictures of what each mode does, see the MDN docs.