Text on canvas is usually painful; Solandra wraps it in the same small, declarative style as everything else. There are three methods:
s.fillText(config, text) — solid text in the current fill colours.drawText(config, text) — outlined text in the current stroke colours.measureText(config, text) — returns TextMetrics so you can lay text out preciselyThe configuration is shared:
type TextConfig = {
at: Point2D // position
size: number // in canvas units, like everything else
align?: "center" | "left" | "right" | "start" | "end" // default "center"
font?: string // font family, defaults to the system font stack
style?: "normal" | "italic" | "oblique"
weight?: "normal" | "bold" | "100" | ... | "900"
variant?: "normal" | "small-caps"
}Crucially size is in the same normalized units as coordinates: 0.1 means a tenth of the canvas width, at any resolution.
s.background(45, 30, 95)
s.setFillColor(215, 70, 30)
s.fillText({ at: [0.5, 0.25], size: 0.15, weight: "bold" }, "Solandra")
s.setStrokeColor(340, 80, 50)
s.lineWidth = 0.002
s.drawText({ at: [0.5, 0.5], size: 0.15, weight: "bold" }, "Solandra")
s.setFillColor(160, 70, 35)
s.fillText({ at: [0.5, 0.72], size: 0.08, style: "italic" }, "generative art")Since text is just another drawing operation, it participates in iteration, randomness and transforms like anything else:
s.background(230, 40, 12)
const glyphs = ["0", "1"]
s.forTiling({ n: 14, type: "square" }, (_pt, [dX], [cX, cY], i) => {
s.setFillColor(120, 80, 30 + s.random() * 45)
s.fillText({ at: [cX, cY], size: dX * 0.8 }, s.sample(glyphs))
})Combined with transforms:
s.background(0, 0, 96)
const word = "spiral"
s.withTranslation([0.5, 0.5], () => {
s.times(40, (n) => {
s.withRotation(n * 0.35, () => {
s.setFillColor(200 + n * 4, 70, 45, 0.9)
s.fillText(
{ at: [0.1 + n * 0.009, 0], size: 0.02 + n * 0.002, align: "left" },
word
)
})
})
})measureText takes the same configuration (minus at) and returns standard TextMetrics, in canvas units. Use it to size backgrounds, underline, or fit text into a box:
s.background(210, 30, 15)
const label = "measured"
const size = 0.12
const m = s.measureText({ size, weight: "bold" }, label)
s.setFillColor(45, 90, 60)
s.fill(
new Rect({
at: [0.5 - m.width / 2 - 0.03, 0.5 - size / 2 - 0.03],
w: m.width + 0.06,
h: size + 0.06,
})
)
s.setFillColor(210, 60, 15)
s.fillText({ at: [0.5, 0.5], size, weight: "bold" }, label)By default Solandra uses a native system font stack (systemFont is exported if you want to extend it). You can use any font available on the page via the font option; just make sure webfonts are loaded before you render.
s.fillText({ at: [0.5, 0.5], size: 0.1, font: "Georgia, serif" }, "Serif!")Solandra was made by James Porter.
Check out the GitHub page or install with npm i solandra