Draw
`<pura-draw>` writes an SVG stroke on as you scroll, the self-drawing line from gsap and motion, driven natively. The path is normalized with `pathLength="1"`, hidden with `stroke-dasharray: 1; stroke-dashoffset: 1`, then the offset ramps to `0` so the line draws itself in. `stroke-dashoffset` is a long-proven animatable property that always re-paints, so this needs no per-frame JS. The default `trigger="scrub"` ties the offset 1:1 to a scroll-driven timeline (`animation-timeline: view()` or `scroll()`); `trigger="view"` draws once when scrolled into view (eased by the spring primitive); `trigger="load"` draws once on connect. SSR and reduced motion are safe: before JS the stroke sits fully drawn so nothing is missing, the scrub block is gated behind `prefers-reduced-motion: no-preference`, and reduced motion shows the finished stroke. The `loop` attribute turns the element into an infinite loading indicator: a stroke segment (sized by `loop-dash`, fraction of the path) chases around the path forever, one lap per `loop-dur` seconds, the dash pattern summing to the full path length so the cycle is seamless. Each instance registers in `window.__puraDraws` by `data-pura-id` with `{ path, replay }`.
Preview
<!-- default: scroll and the wave draws itself in -->
<pura-draw style="width: 200px; height: 200px;"></pura-draw>
<!-- an underline flourish, one-shot on view -->
<pura-draw trigger="view"
path="M 4 14 C 40 26, 120 2, 196 12"
viewbox="0 0 200 20"
stroke="#2563eb" stroke-width="3"
style="width: 200px; height: 20px;"></pura-draw>
<!-- scrub against the page scroll container -->
<pura-draw timeline="scroll" range="cover 0% cover 100%"
path="M 10 90 L 50 10 L 90 90"
style="width: 160px; height: 160px;"></pura-draw>
<!-- infinite loading: a segment chases around the path forever -->
<pura-draw loop loop-dur="1.4" loop-dash="0.25"
path="M 50 10 A 40 40 0 1 1 49.9 10"
stroke="#2563eb" stroke-width="6"
style="width: 64px; height: 64px;"></pura-draw> Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
trigger | "scrub" | "view" | "load" | scrub | scrub ties the stroke 1:1 to a scroll-driven timeline; view draws once when scrolled into view; load draws once on connect. |
timeline | "view" | "scroll" | view | Scrub only. view maps the element's own view progress; scroll maps the nearest scroll container. |
path | string | wave | SVG path d-string to draw on. |
viewbox | string | 0 0 100 100 | SVG viewBox the path is drawn in. |
stroke | string | currentColor | Stroke color. |
stroke-width | number | 4 | Stroke width, in viewBox units. |
fill | string | none | Path fill (usually none for a pure line-draw). |
linecap | "round" | "butt" | "square" | round | Stroke line cap. |
range | string | cover 0% cover 50% | animation-range for the scrub timeline. Default completes the draw as the path reaches viewport center, then holds. |
loop | boolean | false | A stroke segment chases around the path forever (infinite loading). Overrides trigger. |
loop-dur | number | 1.6 | Loop only. Seconds per lap around the path. |
loop-dash | number | 0.3 | Loop only. Visible fraction of the path (0..1) in the chasing segment. |
preset | "default" | "gentle" | "wobbly" | "stiff" | "slow" | "snappy" | default | Spring easing for view/load triggers. Or set stiffness/damping/mass directly. |
Installation
pura is copy-paste and dependency-free. Load the whole library, or just this component.
<link rel="stylesheet" href="/pura/tokens.css">
<script type="module" src="/pura/lib/draw.js"></script> import "./pura/lib/draw.js";