Define a Canvas primitive
Supply synchronous drawing, style normalization, resolution, and opt-in measurement.
APIs demonstrated
Section titled “APIs demonstrated”- definePrimitive from
@pibbl/core - useCanvasContext from
@pibbl/core - SystemStyle from
@pibbl/core - RenderingContext2D from
@pibbl/core - PrimitiveRender from
@pibbl/core - PrimitiveCapabilities from
@pibbl/core - PrimitiveInput from
@pibbl/core - PrimitiveInputOf from
@pibbl/core - PrimitiveProgramProps from
@pibbl/core - PibblPrimitiveComponent from
@pibbl/core - SpecifiedStyle from
@pibbl/core - NormalizedStyle from
@pibbl/core - ResolvedStyle from
@pibbl/core - StyleResolutionContext from
@pibbl/core - MeasureInput from
@pibbl/core - MeasureResult from
@pibbl/core - CanvasMeasurementService from
@pibbl/core
Complete example
Section titled “Complete example”Plain source. Run as a browser module after the document body exists. TSX uses jsx: "react-jsx" and jsxImportSource: "@pibbl/core".
import { pibbl, definePrimitive, useCanvasContext, Text, type SystemStyle, type RenderingContext2D, type PrimitiveRender, type PrimitiveCapabilities, type PrimitiveInput, type PrimitiveInputOf, type PrimitiveProgramProps, type PibblPrimitiveComponent, type SpecifiedStyle, type NormalizedStyle, type ResolvedStyle, type StyleResolutionContext, type MeasureInput, type MeasureResult, type CanvasMeasurementService,} from "@pibbl/core";
type Props = { label: string };interface Style extends SystemStyle { size?: number; color?: string;}const render: PrimitiveRender<Props, Style> = ( props: Readonly<PrimitiveProgramProps<Props>>, style: ResolvedStyle<Style>, context: RenderingContext2D,) => { context.fillStyle = style.color ?? "#087e8b"; context.fillRect(20, 20, style.size ?? 140, 70); return ( <Text style={{ left: 30, top: 60, font: "18px sans-serif", fill: "#fff" }}> {props.label} </Text> );};const capabilities: PrimitiveCapabilities<Props, Style> = { normalizeStyle(style: SpecifiedStyle<Style>): NormalizedStyle<Style> { return { ...style, size: Math.max(40, style.size ?? 140) }; }, resolveStyle( style: NormalizedStyle<Style>, context: Readonly<StyleResolutionContext>, ): ResolvedStyle<Style> { return { ...style, size: Math.min(style.size ?? 140, context.allocation.width), }; }, measure({ props, service }: MeasureInput<Props, Style>): MeasureResult { const measurement: CanvasMeasurementService = service; return { status: "measured", size: { width: measurement.measureText(props.label, "18px sans-serif").width + 40, height: 110, }, }; },};const Badge: PibblPrimitiveComponent< PrimitiveProgramProps<Props>, PrimitiveInput<Props, Style>> = definePrimitive(render, capabilities);const props: PrimitiveInputOf<typeof Badge> = { label: "Custom primitive", style: { size: 240 },};function Scene() { const context = useCanvasContext(); const width = context.measureText("Pibbl").width; return ( <> <Badge {...props} /> <Text style={{ left: 20, top: 140, font: "14px sans-serif", fill: "#222" }} > Measured Pibbl width: {width.toFixed(1)} </Text> </> );}const canvas = document.createElement("canvas");canvas.width = 360;canvas.height = 180;document.body.append(canvas);const controller = pibbl(canvas, <Scene />);window.addEventListener("pagehide", () => controller.dispose(), { once: true });Usage notes
Section titled “Usage notes”- A custom primitive owns its drawing geometry. Use supported hooks for targeting and measurement; never write signals during render. useCanvasContext is for synchronous measurement, not retained context ownership.
Documentation version
Section titled “Documentation version”Documentation built with @pibbl/core 0.0.2, revision 2dccb19. ALPHA — NOT FOR PRODUCTION USE.