@htmlbricks/hb-stylus-paper
v0.73.7
Published
Freehand drawing surface with modes (`draw`, `eraser`, `select`), brush `options`, `background_color` / `pen_color` / `pen_opacity`, and optional `load_draw`, `insert_image`, or `insert_text`. Supports `save_as`, `size`, `view`, `goto`, and events for str
Downloads
11,588
Readme
hb-stylus-paper — integrator guide
Category: inputs · Tags: inputs, drawing · Package: @htmlbricks/hb-stylus-paper · Custom element: hb-stylus-paper
Summary
hb-stylus-paper is a freehand drawing surface backed by SVG and perfect-freehand stroke smoothing. It supports draw, eraser, and select modes, configurable brush options, paper size, optional view (zoom and pan), and serialized payloads for loading drawings, inserting assets, and exporting JSON. Custom events report stroke lifecycle, selection rectangles, history index changes, saves, and load outcomes.
Layout metadata: this component is intended for fullscreen use in catalog tooling; give the host enough height and width for comfortable drawing.
Web component conventions
- Attribute names use snake_case.
- Booleans in HTML use the strings
yesorno(for exampledebug). - Numbers (for example
pen_opacity,goto) are passed as strings that the implementation parses. - Objects (
options,size,view,load_draw,save_as,insert_image,insert_text) must be JSON strings when set from HTML orsetAttribute, matching the shapes described below.
Attributes and props
| Attribute | Required | Description |
|-----------|----------|-------------|
| id | No | Logical instance id; echoed on events and in saved payloads. |
| style | No | Optional host inline style (typed on the component interface). |
| draw_id | No | Stable id for the current drawing session. If omitted, a random value with a timestamp suffix is generated. |
| background_color | No | CSS color for the SVG background (default rgb(255,255,255)). |
| pen_color | No | CSS color used for new strokes (default rgb(0,0,0)). |
| pen_opacity | No | Stroke opacity; string or number coerced with Number() (default 1). |
| mode | No | "draw" (default), "eraser", or "select". Leaving select clears the current selection highlight state. |
| debug | No | "yes" shows a small debug readout (pointer type and button state); any other value is treated as no. |
| options | No | JSON object: brush tuning for perfect-freehand. Defaults include size: 6, thinning: 0.7, smoothing: 0.5, streamline: 0.5. The implementation merges simulatePressure per pointer: false for pointerType === "pen", otherwise true, unless overridden in options. See Brush options. |
| size | No | JSON TSize: requires paperSize (see Paper size); optional width / height for custom layouts. Default { "paperSize": "A4" }. |
| view | No | JSON object: lockPan, lockZoom, zoom: { type: "fit" \| "custom", value: number }, pan: { x, y }. Defaults use fit zoom and zero pan with pan/zoom unlocked. |
| goto | No | History index jump (coerced from string). Negative values reset internal formatting state; values are clamped to the loaded stroke count. After applying, the component emits changeIndex. |
| load_draw | No | JSON TSave document: type, draw (array of IStroke), id, draw_id, name, version, size. When version or draw_id indicates a new document, strokes replace the canvas and drawLoaded / historyIndex fire. |
| save_as | No | JSON { "name": string, "type": TSaveType }. Triggers export when strokes exist. Currently only type: "json" is implemented in the component; other TSaveType values log an error and do not emit save. |
| insert_image | No | JSON { "name": string, "type": "png" \| "svg" \| "jpg" \| "jpeg", "uri"?: string, "base64"?: string }. Raster types enqueue an image stroke using base64 as href. svg is not fully wired (handler is a stub). On supported raster paths, imageLoaded fires with ok: true. Unsupported types do not dispatch imageLoaded. |
| insert_text | No | JSON { "name": string, "content": string }. The handler is currently a stub (logging only); txtLoaded still fires with ok: true. |
Paper size
paperSize accepts the union defined in types/webcomponent.type.d.ts, including ISO sizes (A0–A5, B0–B6), North American names (letter, legal, tabloid, …), and custom. Internal layout logic explicitly maps A3 and A4 (default) to millimetre page dimensions for view calculations; other enum values should still be stored in save payloads for round-tripping.
Brush options
Logical fields follow TPerfectFreeHandOptions: size (required in typings when provided), optional thinning, smoothing, streamline, easing, simulatePressure, and optional start / end taper objects (taper, easing, cap). If options is parsed from JSON and omits numeric defaults, the component fills size, thinning, smoothing, and streamline from built-in defaults.
Interaction notes
- Draw mode: primary button draws; each completed stroke increments the history index and emits
endStrokethenhistoryIndex. - Eraser: use
mode="eraser", or in draw mode use stylus pressure 0 with primary button, or button 32 (middle / auxiliary), to erase by hitting stroke paths. - Select mode: drag a selection rectangle; strokes fully inside the box are marked selected and a
selectionevent is emitted. Dragging inside an existing selection can group strokes into amultiplestrokeentry for moving.
Events
Listen with addEventListener or framework bindings. Payloads match Events in types/webcomponent.type.d.ts.
| Event | detail (logical shape) |
|-------|---------------------------|
| startStroke | { id, draw_id, start: Date, stroke_id, index } — fired when a drawing gesture begins (twice on the first stroke of a session: once when the session start time is fixed, and again with the stroke start). |
| endStroke | { id, draw_id, stroke_id, start, end, min, max, pathData, pen_color, index } — bounding box, SVG path data, and timestamps when the pointer releases after drawing. |
| selection | { id, draw_id, minX, minY, maxX, maxY, strokes: IStroke[] } — rectangle in SVG coordinates and strokes whose bounds lie inside the box (only top-level stroke entries, not nested multipaths, in the strokes array). |
| historyIndex | { id, draw_id, index, start_index? } — history cursor moved (new stroke, erase, load, etc.). |
| changeIndex | { id, draw_id, index, start_index? } — emitted after a goto jump is applied. |
| save | TSave — JSON export: type, draw, id, draw_id, name, version, size (only when save_as.type === "json" is handled). |
| drawLoaded | { id, draw_id, index, start_index? } — after load_draw is applied. |
| imageLoaded | { ok: boolean, error?: string, draw_id, id } — success after a supported raster insert_image; failures for unknown types do not currently emit this event. |
| txtLoaded | { ok: boolean, error?: string, draw_id, id } — emitted after insert_text is processed (implementation stub). |
Authoring types: Events also declares beginStroke ({ date, id, draw_id }). The current component.wc.svelte implementation does not dispatch beginStroke; use startStroke for stroke lifecycle handling.
Styling (Bulma CSS variables)
The component uses Bulma 1.x inside the shadow root. Theme debug chrome by setting these variables on :host or an ancestor so they inherit onto the custom element (see extra/docs.ts).
| Variable | Role |
|----------|------|
| --bulma-size-small | Typography size for the debug banner when debug="yes". |
| --bulma-scheme-main-bis | Background for the debug strip above the drawing surface. |
| --bulma-radius | Corner radius on the debug panel. |
CSS ::part
None.
Slots
None.
Types reference
Stroke records (IStroke), save payloads (TSave, TSaveType), brush options (TPerfectFreeHandOptions), and the full Component / Events maps live in types/webcomponent.type.d.ts beside this package.
Minimal example
<hb-stylus-paper
mode="draw"
pen_color="rgb(0,0,0)"
background_color="rgb(255,255,255)"
pen_opacity="1"
debug="no"
></hb-stylus-paper>Example: brush options and JSON props
<hb-stylus-paper
mode="draw"
options='{"size":16,"thinning":0.7,"smoothing":0.5,"streamline":0.5}'
size='{"paperSize":"A4"}'
></hb-stylus-paper>Example: export JSON after drawing
Set save_as when you want a snapshot (for example from JavaScript after the user clicks Save):
const el = document.querySelector("hb-stylus-paper");
el.setAttribute(
"save_as",
JSON.stringify({ name: "sketch-1", type: "json" }),
);
el.addEventListener("save", (e) => {
console.log(e.detail); // TSave
});Only json export is implemented in the current build; choose other TSaveType values only if a future version adds encoders.
