Documentation
¶
Index ¶
Constants ¶
const MaxLayers = 64
MaxLayers bounds the layers drawn per frame.
Variables ¶
var PanicReporter = func(r any, stack []byte) { fmt.Fprintf(os.Stderr, "webcanvas: recovered panic in a handler: %v\n%s\n", r, stack) }
PanicReporter logs a handler panic the [guard] net recovered. The wasm host swaps in a console.error reporter carrying the JS stack; the default writes to standard error so a native run — and the recover test — still surfaces it. It is a package var, not a const, precisely so run_js.go can replace it.
Functions ¶
This section is empty.
Types ¶
type Animator ¶
type Animator interface {
// AnimationStep advances the scene's animation by dt seconds of real elapsed
// time and reports whether the scene now needs a repaint.
AnimationStep(dt float64) (repaint bool)
}
Animator is an optional companion to App: a scene with time-varying content driven by a REAL wall clock (procedurally animated icons, say) implements it, and [Run] installs a requestAnimationFrame loop that hands it the elapsed dt between frames — in seconds — through AnimationStep. Unlike Ticker (a fixed cadence that always repaints), an Animator advances by the true frame delta and reports whether the frame changed anything, so Run repaints only when a pixel actually moved. A scene that implements neither installs no clock and repaints on input alone. The phase-advance logic lives in the scene (natively testable); only the rAF wiring is browser-side.
type App ¶
type App interface {
// Size returns the fixed pixel dimensions of the scene's surface. The host
// sizes the canvas and allocates the framebuffer from it; it is read once,
// at startup, so it must not change over the App's life.
Size() (w, h int)
// Draw paints the whole scene into buf, a width*height*4 RGBA byte slice
// laid out exactly like an image.RGBA's Pix (row-major, 4 bytes/pixel).
Draw(buf []byte)
// Click delivers a primary (left) button press at (x, y). It begins a
// gesture — a selection, a drag, a placement — that later Move/Release
// calls advance and commit.
Click(x, y int) bool
// Move delivers a pointer move at (x, y). While a Click gesture is in
// flight it is a drag tick; otherwise it is a hover.
Move(x, y int) bool
// Release delivers the primary button release at (x, y), committing any
// in-flight gesture Click began.
Release(x, y int) bool
// Context delivers a secondary (right) button press at (x, y), typically
// opening a context menu. The host suppresses the browser's own menu.
Context(x, y int) bool
// Char delivers a single printable character typed with no Ctrl/Meta/Alt
// modifier — text input for a focused field.
Char(s string) bool
// KeyDown delivers a named key (Enter, Backspace, Delete, Arrow*, …) or a
// modified key press, routed to the focused widget.
KeyDown(s string) bool
}
App is a self-contained canvas scene. A host (the wasm [Run] loop, or a native test) owns the pixel buffer and the event source; the App owns the widgets. Every coordinate is in canvas-local pixels (top-left origin), the same space [Run] derives from the pointer event's position within the canvas' bounding rectangle.
Each event method reports whether the scene changed and therefore needs a repaint, so the host can skip a redraw when nothing moved. Draw is expected to fully paint the buffer (it is never given a dirty region).
type Image ¶ added in v0.6.0
type Image struct {
// contains filtered or unexported fields
}
Image is a browser image the host can draw over the scene: an ImageBitmap, a <canvas>, an OffscreenCanvas, an <img> -- anything CanvasRenderingContext2D drawImage accepts. The scene holds it as an opaque handle: a syscall/js Value in the browser; in a native build or test any value, which the host never draws (there is no canvas to draw it on).
type KeyCodeAware ¶ added in v0.7.0
type KeyCodeAware interface {
KeyCode(code string)
}
KeyCodeAware is an optional companion to App: a scene that implements it is told the physical key of every keyboard event -- the DOM's KeyboardEvent.code ("KeyA", "ArrowUp", "ShiftLeft") -- immediately before the event is delivered (Char, KeyDown, or KeyUp for a KeyUpper). The event's key ("a", "A", "é") is what was typed; its code is where on the keyboard it was, "not affected by the current keyboard layout or modifier state" (W3C UI Events, KeyboardEvent.code) -- what a game or a remote guest that expects key positions needs. A scene that does not implement it sees exactly the events it always did.
type KeyUpper ¶ added in v0.7.0
KeyUpper is an optional companion to App: a scene that implements it is told when a key is released (the DOM's keyup, which a user agent "MUST dispatch ... when a key is released"), with the same key value KeyDown and Char get. [Run] installs the keyup listener only for such a scene. KeyUp reports whether the scene changed.
type Layer ¶ added in v0.6.0
type Layer struct {
Image Image
X, Y, W, H int
// Alpha is the opacity in (0, 1]; 0 (the zero value) means opaque.
Alpha float64
}
Layer is one image drawn over the scene after App.Draw, scaled into its rectangle. Coordinates are canvas pixels, the same space Draw paints: device pixels for a PixelRatioAware scene, so an image lands sharp at any ratio.
type Layered ¶ added in v0.6.0
type Layered interface {
Layers() []Layer
}
Layered is an optional companion to App: a scene that shows images the browser already holds -- frames a GPU rendered into an ImageBitmap, a video -- implements it, rather than copying their pixels through Draw's buffer. After every Draw, [Run] calls Layers and draws each one, in order, with drawImage scaled into its rectangle (smoothing on, quality high). Each layer is drawn under its own recover net, so one unusable image (a closed ImageBitmap throws InvalidStateError) costs that layer only.
The images are the scene's: Run never closes or keeps them past the call. A scene that omits the interface draws exactly as before.
type ModifierAware ¶ added in v0.3.0
type ModifierAware interface {
Modifiers(m Modifiers)
}
ModifierAware is an optional companion to App: a scene that implements it is told the modifier state immediately before every pointer, keyboard and wheel event is delivered. Without it a modified key reaches KeyDown as its bare name -- "a" for Ctrl+A, Cmd+A and Alt+A alike -- and a Shift-click is a click: a scene could not tell copy from select-all, nor extend a selection. A scene that does not implement it sees exactly the events it always did.
type Modifiers ¶ added in v0.3.0
type Modifiers struct {
Ctrl, Shift, Alt, Meta bool
}
Modifiers is the state of the modifier keys when an event happened, as the DOM event reports it (ctrlKey, shiftKey, altKey, metaKey).
type PixelRatioAware ¶ added in v0.5.0
type PixelRatioAware interface {
SetPixelRatio(ratio float64)
}
PixelRatioAware is an optional companion to Resizer: a scene that can render at the display's device pixels implements it. [Run] then sizes the canvas backing store to the canvas' CSS box × window.devicePixelRatio, tells the scene the ratio before each Resize (which is handed device pixels), and re-does both when the ratio changes (browser zoom, a window moved to a display of another density). On a ratio-2 display the scene draws twice the pixels in each direction instead of a 1× bitmap the browser upscales into blocky, blurred text. A go-widgets scene typically forwards the ratio to toolkit.SetMetricScale, which scales every metric and re-renders the OpenType face.
The page must give the canvas its CSS size (e.g. width: 100vw; height: 100vh): the backing store no longer equals it. A scene that omits the interface keeps rendering at CSS pixels, unchanged.
type RepaintAware ¶ added in v0.4.0
type RepaintAware interface {
RepaintWith(request func())
}
RepaintAware is an optional companion to App: a scene whose content can change while nobody touches it -- a fetch that completes, a message that arrives -- and that therefore needs a frame no event will cause.
Before the first frame, [Run] hands it request. Calling request asks for one frame at the next animation frame; it is safe from any goroutine, returns at once, and coalesces: any number of calls before that frame is drawn give one frame. The scene does its pending work at the start of App.Draw -- for a go-widgets tree, draining an mvvm.Queue -- so the frame shows it.
type Resizer ¶
type Resizer interface {
// Resize relays out the scene to fit w×h device pixels and returns the pixel
// size (rw, rh) it will render at — the size the host sizes the canvas and
// framebuffer to.
Resize(w, h int) (rw, rh int)
}
Resizer is an optional companion to App: a scene that can adapt its layout to a NEW surface size implements it, and [Run] installs a window "resize" listener (and fits the canvas to the viewport once at startup) that re-sizes the canvas and framebuffer, calls Resize, and repaints. A scene that omits it keeps the fixed App.Size forever — the pre-resize behaviour every existing demo relies on — so Run never installs the listener and the surface never changes.
Resize is handed the target pixel size (the canvas' laid-out client box) and returns the size it will actually render at: a scene may clamp to a sane minimum, and the host allocates the framebuffer from the RETURNED size, so the scene and the buffer can never disagree. The relayout logic lives in the scene (natively testable); only the DOM resize wiring is browser-side.
type Scroller ¶
type Scroller interface {
// Scroll delivers a wheel / trackpad scroll of dy vertical and dx horizontal
// ROWS at canvas-local (x, y), and reports whether the scene needs a repaint.
Scroll(x, y, dx, dy int) (repaint bool)
}
Scroller is an optional companion to App: a scene with a scrollable region (a docked list, an icon palette, an overflowing panel) implements it, and [Run] installs a "wheel" listener that translates the browser's WheelEvent into toolkit scroll ROWS and routes them — with the canvas-local pointer position, so the scene can hit-test which region the wheel is over — through Scroll. A scene that omits it installs no wheel listener, so the page keeps its default wheel behaviour and every existing demo (the widget gallery) is unchanged.
dx / dy are the horizontal / vertical scroll amounts in toolkit ROWS (already normalised from the event's deltaMode by [scrollRows]): positive dy scrolls down / forward, positive dx scrolls right. A scene typically forwards them to the widget under (x, y) as a [toolkit.Event] of kind EventScroll (Delta = dy, DeltaX = dx), which scrollable widgets clamp at both ends. Scroll reports whether the scene changed and therefore needs a repaint.
type Ticker ¶
type Ticker interface {
// Tick advances one animation frame. Run repaints after every Tick.
Tick()
}
Ticker is an optional companion to App: a scene that needs a steady animation clock (a toast countdown, a blinking caret) implements it, and [Run] installs a 60-Hz timer that calls Tick and repaints. A scene with no time-varying state omits it, and Run installs no timer, so it never repaints except in response to input.