canvas
Low-level drawing, into a <canvas>.
HTML and CSS describe a document, and a document is described rather than drawn. This is the other door: a chart, a game or a visualisation knows where its own marks go, and needs to put them there without describing anything first.
It is a named surface over the drawing calls, not a graphics library. Every function here is one of the calls a renderer implements, or something assembled from them — and where a shape cannot be assembled, it is absent rather than approximated. There is no line, no path and no polygon, because the boundary has no line, path or polygon and faking one out of rectangles would produce something that looked wrong on every backend in a different way. What is here is exact everywhere.
Coordinates are floats, with the origin at the top left of the surface. Colours are 0xRRGGBB.
use std/canvas
fn chart(x: float, y: float, w: float, h: float) {
canvas.fill(x, y, w, h, 0x101216)
canvas.circle(x + w / 2.0, y + h / 2.0, 40.0, 0x3b82f6)
}
The surface is the host's, and there is one. These calls are compiler builtins that go to whatever surface the runtime supplies, so a program cannot yet say which <canvas> it is drawing into. Choosing one means passing a dom.Element through to the drawing builtins, which is a change to the boundary rather than to this file.
fill
pub fn fill(x: float, y: float, w: float, h: float, colour: int)
A filled rectangle.
rounded
pub fn rounded(x: float, y: float, w: float, h: float, radius: float, colour: int)
A filled rectangle with rounded corners.
The radius is clamped by the renderer to half the shorter side, so a radius larger than the box gives a stadium rather than an error.
circle
pub fn circle(cx: float, cy: float, radius: float, colour: int)
A circle centred on (cx, cy).
A rounded rectangle as wide as it is tall, with the radius at half the side — which is a circle exactly, on every backend, because that is the case the clamp in rrect was already handling.
ring
pub fn ring(x: float, y: float, w: float, h: float, radius: float, width: float, colour: int)
The ring between a box and the same box inset by width — a border, drawn without filling the middle.
This is what makes an outline independent of a fill: the two are separate calls, so an outlined shape does not have to paint its own interior in whatever colour it happens to be sitting on.
circle_ring
pub fn circle_ring(cx: float, cy: float, radius: float, width: float, colour: int)
The ring of a circle: an outlined circle of the given stroke width.
text
pub fn text(x: float, y: float, body: str, colour: int)
A run of text, with (x, y) at the top left of its line box.
One run on one line: this draws what it is given and does not wrap. Wrapping is a measurement rather than a drawing decision, so it is text.wrap's: hand it [width_of] as the measurement and draw the lines it answers, one [line_height] apart.
font
pub fn font(size: float, weight: int)
Select the size and weight that subsequent text is drawn and measured in.
Measurement follows the same selection, which is what keeps a caller's own layout arithmetic agreeing with what appears.
width_of
pub fn width_of(body: str) -> float
How wide a run is in the font currently selected.
line_height
pub fn line_height() -> float
How tall one line is in the font currently selected: ascent plus descent plus leading.
image
pub fn image(x: float, y: float, w: float, h: float, src: str)
A picture, fitted into the box without distorting it.
src is whatever the host can resolve — a URL, a path, a data: URI. A backend that has not loaded it yet draws nothing and asks to be called again when it has; a backend that cannot draw pictures at all records that one goes here.
clip
pub fn clip(x: float, y: float, w: float, h: float)
Confine everything drawn until [unclip] to a rectangle.
Clips nest, and every one has to be closed. Nothing closes one for you: a caller that clips and returns leaves everything drawn afterwards confined to a box it has forgotten about.
unclip
pub fn unclip()
alpha
pub fn alpha(a: float)
Draw everything after this at a of full opacity, from 0.0 to 1.0.
It applies until it is set back. A caller that sets it and returns leaves every later mark translucent, which is a bug that shows up somewhere else entirely — so set it back explicitly.
opaque
pub fn opaque()