Skip to content

Scene

A Scene is a specialized Group that binds to a Context and manages the full rendering lifecycle. It handles clearing, rendering in z-index order, automatic resize re-rendering, and DOM event delegation for pointer interactivity.

Creating a Scene

A scene can be created from a CSS selector, an HTMLElement, or an existing Context:

ts
import {
    createScene,
} from '@ripl/core';

// From a CSS selector (creates a canvas context automatically)
const scene = createScene('.my-container');

// From an existing context
const scene = createScene(context);

// With initial children
const scene = createScene('.my-container', {
    children: [circle, rect],
});

Options

OptionTypeDefaultDescription
childrenElement | Element[][]Initial child elements
renderOnResizebooleantrueAutomatically re-render when the context resizes
All Group optionsInherited from Group

Properties

PropertyTypeDescription
contextContextThe bound rendering context
widthnumberShortcut for context.width
heightnumberShortcut for context.height
bufferElement[]The flattened, z-sorted render buffer

Rendering

Call render() with no arguments — the scene uses its bound context:

ts
scene.render();

This clears the context, marks the render start, renders all buffered elements in z-index order, and marks the render end. You don't need to manage any of this yourself.

Render Buffer

When elements are added to or removed from the scene (or any nested group), the scene automatically rebuilds a flat render buffer. This buffer is a sorted array of all leaf elements in the scene graph, ordered by zIndex. This hoisting converts rendering from a recursive tree walk (O(n^c)) to a simple flat iteration (O(n)).

Events

The scene extends the Group event system with DOM event delegation. It listens for mouse events on the context's DOM element and dispatches them to the appropriate Ripl elements based on hit testing.

Supported Pointer Events

EventDescription
clickElement was clicked
mouseenterPointer entered the element
mouseleavePointer left the element
mousemovePointer moved within the element
ts
const circle = createCircle({
    fillStyle: '#3a86ff',
    cx: 100,
    cy: 100,
    radius: 50,
});

const scene = createScene('.container', {
    children: [circle],
});

scene.render();

// Now pointer events work on the circle
circle.on('click', (event) => {
    console.log('Clicked!', event.data.x, event.data.y);
});

circle.on('mouseenter', () => {
    circle.fillStyle = '#ff006e';
    scene.render();
});

circle.on('mouseleave', () => {
    circle.fillStyle = '#3a86ff';
    scene.render();
});

Scene-Level Events

The scene also emits its own events:

EventDescription
resizeThe context was resized
mouseenterPointer entered the scene area
mouseleavePointer left the scene area
mousemovePointer moved within the scene area

NOTE

Pointer events on individual elements only work when those elements are inside a Scene. The scene is responsible for DOM event delegation and hit testing.

Automatic Resize

By default (renderOnResize: true), the scene automatically re-renders when the context resizes. Disable this if you want to handle resize manually:

ts
const scene = createScene('.container', {
    renderOnResize: false,
});

scene.on('resize', () => {
    // Custom resize handling
    scene.render();
});

Cleanup

Call destroy() to remove all DOM event listeners, destroy the context, and clean up:

ts
scene.destroy();

Demo

Hover over the elements to see pointer events in action. Click an element to change its color.