Canvas Element
On of the strenghts of QML is its closeness to the Javascript ecosystem. This lets us reuse existing solutions from the web world and combine it with the native performance of QML visuals. However, sometimes we want to reuse graphics solutions from the web space too. That is where the Canvas
element comes in handy. The canvas element provides an API very closely aligned to the drawing APIs for the identically named HTML element.
The fundamental idea of the canvas element is to render paths using a context 2D object. The context 2D object, contains the necessary graphics functions, whereas the canvas acts as the drawing canvas. The 2D context supports strokes, fills gradients, text and a different set of path creation commands.
Let’s see an example of a simple path drawing:
import QtQuick
Canvas {
id: root
// canvas size
width: 200; height: 200
// handler to override for drawing
onPaint: {
// get context to draw with
var ctx = getContext("2d")
// setup the stroke
ctx.lineWidth = 4
ctx.strokeStyle = "blue"
// setup the fill
ctx.fillStyle = "steelblue"
// begin a new path to draw
ctx.beginPath()
// top-left start point
ctx.moveTo(50,50)
// upper line
ctx.lineTo(150,50)
// right line
ctx.lineTo(150,150)
// bottom line
ctx.lineTo(50,150)
// left line through path closing
ctx.closePath()
// fill using fill style
ctx.fill()
// stroke using line width and stroke style
ctx.stroke()
}
}
This produces a filled rectangle with a starting point at 50,50 and a size of 100 and a stroke used as a border decoration.
The stroke width is set to 4 and uses a blue color define by strokeStyle
. The final shape is set up to be filled through the fillStyle
to a “steel blue” color. Only by calling stroke
or fill
the actual path will be drawn and they can be used independently from each other. A call to stroke
or fill
will draw the current path. It’s not possible to store a path for later reuse only a drawing state can be stored and restored.
In QML the Canvas
element acts as a container for the drawing. The 2D context object provides the actual drawing operation. The actual drawing needs to be done inside the onPaint
event handler.
Canvas {
width: 200; height: 200
onPaint: {
var ctx = getContext("2d")
// setup your path
// fill or/and stroke
}
}
The canvas itself provides a typical two-dimensional Cartesian coordinate system, where the top-left is the (0,0) point. A higher y-value goes down and a hight x-value goes to the right.
A typical order of commands for this path based API is the following:
- Setup stroke and/or fill
- Create path
- Stroke and/or fill
onPaint: {
var ctx = getContext("2d")
// setup the stroke
ctx.strokeStyle = "red"
// create a path
ctx.beginPath()
ctx.moveTo(50,50)
ctx.lineTo(150,50)
// stroke path
ctx.stroke()
}
This produces a horizontal stroked line from point P1(50,50)
to point P2(150,50)
.
TIP
Typically you always want to set a start point when you reset your path, so the first operation after beginPath
is often moveTo
.