Shape
A Shape is a specialized Element that draws using a path. While Element is the base class for all drawable objects, Shape adds path-based rendering with automatic fill and stroke, plus pixel-accurate hit testing via the path geometry.
Most built-in elements (Circle, Rect, Arc, Line, Polygon, Polyline, Ellipse) extend Shape. The notable exception is Text, which extends Element directly.
Element vs Shape
| Feature | Element | Shape |
|---|---|---|
| Base class | EventBus | Element |
| Rendering | Manual (override render) | Path-based (override render with path callback) |
| Auto fill/stroke | No | Yes — fills and strokes automatically based on style |
| Hit testing | Bounding box only | Pixel-accurate via path geometry |
| Use case | Text, images, custom non-path elements | Geometric shapes with fill/stroke |
Shape Options
In addition to all Element options, shapes accept:
| Option | Type | Default | Description |
|---|---|---|---|
autoFill | boolean | true | Automatically fill the path if fillStyle is set |
autoStroke | boolean | true | Automatically stroke the path if strokeStyle is set |
const circle = createCircle({
fillStyle: '#3a86ff',
strokeStyle: '#1a56db',
autoStroke: false, // Don't stroke, only fill
cx: 100,
cy: 100,
radius: 50,
});How Shape Rendering Works
When you call shape.render(context), the following happens:
- The element's
rendermethod saves the context state - All style properties (fillStyle, lineWidth, etc.) are applied to the context
- A new path is created via
context.createPath() - The shape's drawing callback builds the path geometry (e.g.
path.circle(x, y, r)) - If
autoFillistrueandfillStyleis set, the path is filled - If
autoStrokeistrueandstrokeStyleis set, the path is stroked - The context state is restored
This is why shapes "just work" — you set fillStyle and/or strokeStyle and the shape handles the rest.
Hit Testing
Shapes provide pixel-accurate hit testing through the intersectsWith method. Instead of using a simple bounding box check (like the base Element), shapes test whether a point is inside the actual path geometry:
// Pixel-accurate — tests against the actual circle path
circle.intersectsWith(mouseX, mouseY);The pointerEvents property controls which parts of the shape respond to hits:
'all'— fill area OR stroke area (default)'fill'— only the fill area'stroke'— only the stroke area'none'— no hit testing
Demo
The demo below shows the difference between autoFill and autoStroke. The left circle has both enabled, the middle has only fill, and the right has only stroke.