Skip to content
GitHub repository
Building UI

Hit testing & events

Pointer events test each shape's real distance field: rings are hollow, lines react only within their stroke and rounded corners stay rounded.

Updated pixi-silk 0.1.0

Source src/demos/hit-test.demo.ts
import { SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';

// containsPoint() evaluates the real distance field: the ring is hollow, the line is only its stroke.
export default defineDemo({
    size: [480, 220],
    setup({ stage, tick }) {
        const items = [
            { draw: (g: SilkGraphics, c: number) => g.circle(100, 110, 60).stroke({ width: 18, color: c }) },
            { draw: (g: SilkGraphics, c: number) => g.roundRect(200, 50, 120, 120, 36, 0.6).fill(c) },
            {
                draw: (g: SilkGraphics, c: number) =>
                    g.polyline([350, 170, 390, 60, 450, 150]).stroke({ width: 10, cap: 'round', color: c }),
            },
        ].map((item) => {
            const g = new SilkGraphics();

            g.eventMode = 'static';
            g.cursor = 'pointer';
            stage.addChild(g);

            return { ...item, g, hover: false };
        });

        for (const item of items) {
            item.g.on('pointerover', () => (item.hover = true));
            item.g.on('pointerout', () => (item.hover = false));
        }
        tick(() => {
            for (const item of items) item.draw(item.g.clear(), item.hover ? 0x30d158 : 0x48484a);
        });
    },
});
Hover the shapes. The ring's hole and the space around the line don't count.

Set eventMode and add listeners, as with any Pixi display object:

const button = new SilkGraphics();

button.eventMode = 'static';
button.cursor = 'pointer';
button.on('pointertap', () => console.log('tap'));
button.roundRect(0, 0, 160, 48, 24).fill(0x0a84ff);

containsPoint() first checks the object’s bounds. Then it runs the shader’s distance functions on the CPU for the primitives under the pointer, last drawn first. A point is inside when its distance to the painted region is negative:

  • A stroked circle is a ring, so its centre is not inside.
  • A line or polyline counts only within its stroke width.
  • A rounded rect excludes the corners outside its radius.
  • A blurred paint counts only up to its geometric edge.

You can also call it yourself:

if (g.containsPoint(g.toLocal(pointerGlobal))) { /* hovered */ }

In large interactive scenes, split independent targets into separate SilkGraphics objects. Then each one hit-tests only its own primitives.

API reference: SilkGraphics