astwerk

wasmwrap — canvas

Typed 2D drawing — Canvas, Ctx2D, animation loops.

The one place wasmwrap adds a real API surface: 2D drawing with typed calls instead of raw getContext("2d") juggling. The rest of the wrapper is on wasmwrap.

Documentation

Canvas

canvas := wasmwrap.Query("#c").AsCanvas()
ctx := canvas.Context2D()

AsCanvas wraps a <canvas> element. Canvas then offers:

Method Does
Context2D() the drawing context everything below lives on
Size() / SetSize(w, h) reads and sets the drawing buffer
Element() the element back, for styling or events
FitToDisplay(ctx) sizes the buffer to the element's CSS size and returns the logical size

FitToDisplay

canvas := wasmwrap.Query("#c").AsCanvas()
ctx := canvas.Context2D()
w, h := canvas.FitToDisplay(ctx)

Sizes the drawing buffer to the element's CSS size times the device pixel ratio and returns the logical size to draw against.

Why skipping it makes drawings blurry

A canvas has two sizes that are easy to confuse: the width/height attributes are the pixel buffer, and the CSS size is the display size. If they differ — which they do on any high-DPI screen — the browser stretches the buffer to fit, and every line gets smeared across pixels. FitToDisplay aligns the buffer with the display so each buffer pixel is one screen pixel.

Drawing

ctx.FillStyle("#4f7cff").
	BeginPath().
	Arc(w/2, h/2, 40, 0, 2*math.Pi).
	Fill()

Everything chains, and state setters (FillStyle, StrokeStyle, LineWidth, Font) apply until changed. Path building is the usual BeginPath / MoveTo / LineTo / Arc / ClosePath, finished by Fill or Stroke. FillText draws text and MeasureText returns its width.

The full Ctx2D surface
ctx.StrokeStyle(color).LineWidth(w).Font(f)
ctx.Save().Restore()
ctx.FillRect(x, y, w, h).StrokeRect(x, y, w, h)
ctx.BeginPath().MoveTo(x, y).LineTo(x, y).Arc(x, y, r, a, b).ClosePath()
ctx.Fill().Stroke()
ctx.FillText(text, x, y)
w := ctx.MeasureText(text)

Everything chains and everything that can't be expressed drops to ctx.Value().Call(...).

Animation

cancel := ctx.RequestAnimationFrame(func(dt float64) {
	elapsed += dt
	ctx.Clear(0, 0, w, h)
	// draw
})

RequestAnimationFrame drives a loop rather than a single frame — it reschedules itself and hands you the delta in seconds. The cancel it returns stops the loop.

Example

//go:build js && wasm

package main

import (
	"math"

	"github.com/LukasDerBaum42/astwerk/wasmwrap"
)

func main() {
	canvas := wasmwrap.Query("#c").AsCanvas()
	ctx := canvas.Context2D()
	w, h := canvas.FitToDisplay(ctx)

	angle := 0.0
	ctx.RequestAnimationFrame(func(dt float64) {
		angle += dt
		ctx.Clear(0, 0, w, h)
		ctx.FillStyle("#4f7cff").
			BeginPath().
			Arc(w/2+math.Cos(angle)*50, h/2+math.Sin(angle)*50, 30, 0, 2*math.Pi).
			Fill()
	})
	select {}
}

A dot orbiting the canvas centre, driven by the animation loop. The same pattern — clear, draw, repeat — is the skeleton of any canvas demo.