ef-canvas
Attributes
data-element-id-attributestringAttribute name for element identification
data-element-idenable-transform-handlesbooleanShow transform handles for selected elements
trueProperties
selectionContextSelectionContextSelection state and methods (provided via Lit context)
highlightedElementHTMLElement | nullCurrently highlighted (hovered) element
activeRootTemporalTemporalMixinInterface & HTMLElement | nullRoot temporal element containing current selection
Methods
registerElement(element: HTMLElement, id?: string): stringRegister an element for canvas management
Element IDunregisterElement(element: HTMLElement | string): voidUnregister an element
tryRegisterElement(element: HTMLElement): voidTry to register element, auto-generating ID if needed
updateElementPosition(elementId: string, x: number, y: number): voidUpdate element position in canvas coordinates
getElementData(elementId: string): CanvasElementData | nullGet element metadata
CanvasElementData | nullgetAllElementsData(): CanvasElementData[]Get metadata for all elements
CanvasElementData[]screenToCanvasCoords(screenX: number, screenY: number): { x: number, y: number }Convert screen coordinates to canvas coordinates
canvasToScreenCoords(canvasX: number, canvasY: number): { x: number, y: number }Convert canvas coordinates to screen coordinates
setHighlightedElement(element: HTMLElement | null): voidSet the highlighted (hovered) element
Interactive canvas for managing, selecting, and manipulating elements with drag-and-drop and transform handles.
Basic Usage
Canvas automatically manages any child elements:
<div class="relative w-full h-[400px] border border-gray-300 rounded overflow-hidden"><ef-pan-zoom class="w-full h-full"><ef-canvas class="w-[1200px] h-[800px] bg-gray-100"><div id="box-1" class="absolute top-8 left-8 w-32 h-32 bg-blue-500 text-white flex items-center justify-center rounded">Box 1</div><div id="box-2" class="absolute top-48 left-48 w-32 h-32 bg-green-500 text-white flex items-center justify-center rounded">Box 2</div><div id="box-3" class="absolute top-8 left-48 w-32 h-32 bg-red-500 text-white flex items-center justify-center rounded">Box 3</div></ef-canvas></ef-pan-zoom></div>
Click to select, drag to move, use handles to resize and rotate.
Selection
Canvas provides multiple selection modes:
Single Selection
Click an element to select it:
const canvas = document.querySelector('ef-canvas');// Listen for selection changescanvas.selectionContext.addEventListener('selectionchange', () => {const selected = Array.from(canvas.selectionContext.selectedIds);console.log('Selected:', selected);});
Multi-Selection
Hold Shift to add to selection:
- Click: Select single element (clear others)
- Shift + Click: Add element to selection
- Ctrl/Cmd + Click: Toggle element selection
Box Selection
Click and drag on empty space to select multiple elements:
<div class="relative w-full h-[400px] border border-gray-300 rounded overflow-hidden"><ef-pan-zoom class="w-full h-full"><ef-canvas class="w-[1200px] h-[800px] bg-gray-100"><div id="item-1" class="absolute top-12 left-12 w-24 h-24 bg-blue-500 rounded"></div><div id="item-2" class="absolute top-12 left-48 w-24 h-24 bg-green-500 rounded"></div><div id="item-3" class="absolute top-12 left-84 w-24 h-24 bg-red-500 rounded"></div><div id="item-4" class="absolute top-48 left-12 w-24 h-24 bg-purple-500 rounded"></div><div id="item-5" class="absolute top-48 left-48 w-24 h-24 bg-yellow-500 rounded"></div><div id="item-6" class="absolute top-48 left-84 w-24 h-24 bg-pink-500 rounded"></div></ef-canvas></ef-pan-zoom></div>
Drag on empty space to draw selection box. Hold Shift to add to existing selection.
Drag and Drop
Canvas supports dragging selected elements:
Single Element Drag
Click and drag an element to move it:
// Element position updates automatically// No event handlers needed
Multi-Element Drag
Select multiple elements and drag any selected element to move all:
const canvas = document.querySelector('ef-canvas');// Select multiple elementscanvas.selectionContext.select('box-1');canvas.selectionContext.addToSelection('box-2');canvas.selectionContext.addToSelection('box-3');// Drag any selected element - all move together
Drag Threshold
Canvas uses a 5-pixel drag threshold to distinguish clicks from drags.
Transform Handles
Canvas shows transform handles for selected elements:
Single Selection
Handles for resize and rotate:
- Corner handles: Resize proportionally
- Edge handles: Resize in one dimension
- Rotation handle: Rotate around center
Multi-Selection
Handles for the bounding box:
- Corner handles: Scale all elements proportionally
- Rotation handle: Rotate all elements around group center
Disable Handles
<ef-canvas enable-transform-handles="false"><!-- No transform handles shown --></ef-canvas>
Programmatic Control
Control canvas selection via JavaScript:
const canvas = document.querySelector('ef-canvas');const { selectionContext } = canvas;// Select single elementselectionContext.select('element-id');// Add to selectionselectionContext.addToSelection('another-id');// Toggle selectionselectionContext.toggle('element-id');// Clear selectionselectionContext.clear();// Get selected IDsconst selected = Array.from(selectionContext.selectedIds);
Element Registration
Canvas automatically registers child elements:
Automatic Registration
All child elements are auto-registered with auto-generated IDs:
<ef-canvas><div>Auto-registered with generated ID</div></ef-canvas>
Manual Registration
Provide explicit IDs for stable references:
<ef-canvas><div id="my-element">Registered as 'my-element'</div></ef-canvas>
Programmatic Registration
Register elements via JavaScript:
const canvas = document.querySelector('ef-canvas');const element = document.createElement('div');// Register with auto-generated IDcanvas.tryRegisterElement(element);// Register with explicit IDconst id = canvas.registerElement(element, 'my-custom-id');// Unregistercanvas.unregisterElement('my-custom-id');
Position Management
Update element positions in canvas coordinates:
const canvas = document.querySelector('ef-canvas');// Update position (canvas coordinates)canvas.updateElementPosition('element-id', 100, 200);// Get element metadataconst data = canvas.getElementData('element-id');console.log('Position:', data.x, data.y);console.log('Size:', data.width, data.height);console.log('Rotation:', data.rotation);// Get all elementsconst allElements = canvas.getAllElementsData();
Coordinate Conversion
Convert between screen and canvas coordinates:
const canvas = document.querySelector('ef-canvas');// Screen to canvasconst canvasPos = canvas.screenToCanvasCoords(clientX, clientY);// Canvas to screenconst screenPos = canvas.canvasToScreenCoords(canvasX, canvasY);
Coordinates account for pan/zoom transforms.
Hover Highlighting
Canvas tracks hovered elements:
const canvas = document.querySelector('ef-canvas');// Get highlighted elementconsole.log(canvas.highlightedElement);// Programmatically highlightconst element = document.getElementById('my-element');canvas.setHighlightedElement(element);// Clear highlightcanvas.setHighlightedElement(null);
Highlighted elements get data-highlighted attribute for styling:
[data-highlighted] {outline: 2px solid blue;}
Selection Styling
Selected elements get data-selected attribute:
[data-selected] {outline: 2px solid #2196f3;}
Pan/Zoom Integration
Canvas works seamlessly with ef-pan-zoom:
<ef-pan-zoom><ef-canvas><!-- Elements stay correctly positioned during pan/zoom --></ef-canvas></ef-pan-zoom>
Canvas automatically:
- Converts coordinates accounting for zoom
- Updates transform handles
- Maintains correct hit testing
Active Root Temporal
Canvas tracks the root temporal element containing selection:
const canvas = document.querySelector('ef-canvas');// Get active root temporalconsole.log(canvas.activeRootTemporal);// Listen for changescanvas.addEventListener('activeroottemporalchange', (e) => {console.log('Active root:', e.detail.activeRootTemporal);});
Useful for timeline scrubbing and playback controls.
Nested Elements
Canvas supports nested element hierarchies:
<ef-canvas><div id="parent" class="absolute top-0 left-0 w-64 h-48"><div id="child" class="absolute top-4 left-4 w-32 h-24"><!-- Nested element --></div></div></ef-canvas>
Both parent and child are selectable and draggable.
Element Metadata
Canvas tracks element bounds and transforms:
interface CanvasElementData {id: string;element: HTMLElement;x: number; // Top-left x in canvas coordinatesy: number; // Top-left y in canvas coordinateswidth: number; // Width in canvas unitsheight: number; // Height in canvas unitsrotation?: number; // Rotation in degrees}
Metadata updates automatically when elements change.
Coordinate System
Canvas uses a unified coordinate calculation:
- Dimensions: From
offsetWidth/offsetHeight(unaffected by transforms) - Center: From
getBoundingClientRect()center (rotation-invariant) - Top-left: Calculated from center minus half dimensions
This approach works correctly for rotated, scaled, and nested elements.
Performance
Canvas optimizes for interactive editing:
- Single RAF loop for overlays and handles
- Pointer capture during drag operations
- Efficient hit testing with z-order respect
- No change detection overhead
Deprecated: ef-canvas-item
The ef-canvas-item wrapper is deprecated. Use plain HTML elements:
<!-- Old (deprecated) --><ef-canvas><ef-canvas-item id="item-1"><div>Content</div></ef-canvas-item></ef-canvas><!-- New (recommended) --><ef-canvas><div id="item-1">Content</div></ef-canvas>
All DOM nodes in canvas are now automatically selectable.
Events
activeroottemporalchange
Fired when active root temporal changes. See the Active Root Temporal section above.