dytools-canvas-engine
v1.6.3
Published
A powerful, extensible HTML5 canvas-based engine for building interactive visual applications. Built with TypeScript, Canvas Engine provides a complete event-driven architecture with support for interactive objects, gestures, tools, and multi-mode interac
Readme
Canvas Engine
A powerful, extensible HTML5 canvas-based engine for building interactive visual applications. Built with TypeScript, Canvas Engine provides a complete event-driven architecture with support for interactive objects, gestures, tools, and multi-mode interactions.
Features
- 🎯 Interactive Objects - Create zones with drag, resize, and selection capabilities
- 🖱️ Rich Gesture Recognition - Built-in support for clicks, drags, and complex multi-touch gestures
- 🔧 Extensible Tool System - Modular tools for selection, drawing, lasso selection, zooming, and more
- 🎨 Flexible Rendering Pipeline - Customizable rendering with support for multiple render nodes
- 📐 Viewport Management - Pan and zoom with configurable limits and controls
- 🎭 Mode System - Switch between different interaction modes (select, draw, etc.)
- 📡 Event-Driven Architecture - Clean separation between internal canvas events and external engine events
- 🐛 Debug Support - Built-in debugging options for event logging
- 🎪 Background Images - Support for custom background images with proper scaling
Installation
npm install dytools-canvas-engineQuick Start
import { CanvasEngine, Zone } from 'dytools-canvas-engine';
// Create a canvas engine attached to a DOM element
const engine = new CanvasEngine('#app', {
initialZoom: 1,
initialPan: { x: 10, y: 10 },
defaultMode: 'select'
});
// Add an interactive zone
const zone = new Zone(
'zone-1', // id
100, 100, // x, y
200, 150, // width, height
'#3498db', // color
true, // selectable
true, // movable
true // resizable
);
engine.addObject(zone);
// Listen to engine events
engine.on('object:selected', (payload) => {
console.log('Object selected:', payload.object.id);
});
engine.on('object:moved', (payload) => {
console.log('Object moved to:', payload.newPoint);
});
// Start the engine
engine.start();Core Concepts
Interactive Objects
Interactive objects are entities that can be rendered and interacted with on the canvas. The primary built-in object type is Zone:
const zone = new Zone('my-zone', x, y, width, height, color, selectable, movable, resizable);
// Configure zone properties
zone.name = 'My Zone';
zone.showName = true;
zone.nameAnchor = 'TopLeft';
zone.zIndex = 10;
zone.visible = true;Modes
Modes define different interaction contexts with their own tools and behaviors:
engine.defineMode('draw', {
tools: ['drawing'],
renderers: []
});
engine.defineMode('select', {
tools: ['select', 'move', 'resize', 'hover'],
renderers: []
});
// Switch between modes
engine.switchMode('draw');Built-in Tools
Canvas Engine comes with several pre-built tools:
- SelectTool - Click to select objects
- MoveTool - Drag to move selected objects
- ResizeTool - Resize objects via handles
- HoverTool - Track mouse hover state
- DrawingTool - Draw new rectangular zones
- LassoTool - Select multiple objects with lasso
- ZoomTool - Mouse wheel and pinch-to-zoom
- PanTool - Pan the viewport
- ApiTool - Handle object property changes via API calls
Custom Tools
Create your own tools by implementing the ToolPipelineNode interface:
class CustomTool implements ToolPipelineNode {
processToolEvent(
evt: CanvasEvent,
emit: (evt: CanvasEvent) => void,
emitExternal: <K extends keyof EngineEventPayloads>(type: K, payload: EngineEventPayloads[K]) => void,
engine: CanvasEngine,
toolLock: ToolLock | null
) {
if (evt.type === 'pointerdown') {
// Handle pointer down event
console.log('Pointer down at:', evt.data.point);
}
}
}
// Register and use the tool
engine.registerTool('custom', () => new CustomTool());
engine.defineMode('custom-mode', {
tools: ['custom'],
renderers: []
});Event System
Canvas Engine has two types of events:
Canvas Events (Internal)
Low-level events flowing through the event pipeline:
pointerdown,pointermove,pointerup,pointercanceltouchstart,touchmove,touchend,touchcancelwheel,windowresizedrag,dragstart,dragend,dragpotentialclick
Engine Events (External)
High-level events emitted to external listeners:
// Object events
engine.on('object:selected', (payload) => { });
engine.on('object:deselected', (payload) => { });
engine.on('object:moved', (payload) => { });
engine.on('object:resized', (payload) => { });
engine.on('object:colorchanged', (payload) => { });
engine.on('object:added', (payload) => { });
engine.on('object:removed', (payload) => { });
// Viewport events
engine.on('viewport:changed', (payload) => { });
// Engine events
engine.on('engine:started', (payload) => { });
engine.on('engine:modechanged', (payload) => { });
engine.on('engine:backgroundchanged', (payload) => { });Viewport Controls
// Pan the viewport
engine.setPan(new Vector(100, 100));
// Zoom the viewport
engine.setZoom(2.0);
// Get current viewport state
const pan = engine.pan;
const zoom = engine.zoom;
// Coordinate conversion
const worldPoint = engine.screenToWorld(new Point(100, 100));
const screenPoint = engine.worldToScreen(new Point(50, 50));Object Management
// Add objects
engine.addObject(zone);
// Remove objects
engine.removeObject(zone);
// Get all objects
const objects = engine.getObjects();
// Object flags (for custom state management)
engine.addFlag(zone, 'highlighted');
engine.removeFlag(zone, 'highlighted');
engine.clearFlags(zone);
// Check selection state
const isSelected = engine.hasFlag(zone, 'selected');Background Images
// Set a background image
await engine.setBackgroundFromUrl('https://example.com/image.jpg');
// Or from a File/Blob
await engine.setBackground(imageBlob);Configuration Options
interface CanvasEngineOptions {
initialZoom?: number; // Default: 1
initialPan?: { x: number; y: number }; // Default: { x: 10, y: 10 }
defaultMode?: string; // Default: "default"
addTimerEventSource?: boolean; // Default: false
proxyObjectsForEvents?: boolean; // Default: true
zoomOptions?: {
wheelZoomFactor?: number; // Default: 0.1
pinchZoomFactor?: number; // Default: 0.01
minZoom?: number; // Default: 0.1
maxZoom?: number; // Default: 10
};
// Debug options
debugLogCanvasEvents?: boolean; // Default: false
debugLogEngineEvents?: boolean; // Default: false
}Debug Mode
Enable debug logging to track events:
const engine = new CanvasEngine('#app', {
debugLogCanvasEvents: true, // Log internal canvas events
debugLogEngineEvents: true // Log external engine events
});
// Console output:
// CanvasEvent: pointerdown { type: 'pointerdown', data: {...} }
// EngineEvent: object:selected { object: {...} }Advanced Usage
Custom Renderers
Implement custom rendering logic:
class CustomRenderer implements RenderPipelineNode {
render(ctx: CanvasRenderingContext2D, engine: CanvasEngine) {
// Custom rendering code
ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 100, 100);
}
}
engine.addRenderer(new CustomRenderer());Gesture Recognizers
Create custom gesture recognizers:
class CustomGestureRecognizer implements GestureRecognizer {
process(evt: CanvasEvent, emit: (evt: CanvasEvent) => void) {
// Process events and emit custom gestures
if (evt.type === 'pointerdown') {
// Detect custom gesture
emit({ type: 'customgesture', data: { } });
}
}
}
engine.addRecognizer(new CustomGestureRecognizer());Event Sources
Add custom event sources:
class CustomEventSource implements EventSource {
attach(emit: (evt: CanvasEvent) => void, engine: CanvasEngine) {
// Set up event listeners
window.addEventListener('keydown', (e) => {
emit({ type: 'keydown', data: { key: e.key } });
});
}
detach() {
// Clean up listeners
}
}
engine.addEventSource(new CustomEventSource());API Reference
CanvasEngine
Methods
start()- Start the engineaddObject(obj)- Add an interactive objectremoveObject(obj)- Remove an interactive objectgetObjects()- Get all objectssetPan(vector)- Set viewport pansetZoom(zoom)- Set viewport zoomswitchMode(name)- Switch to a different modedefineMode(name, config)- Define a new moderegisterTool(name, factory, singleton)- Register a custom tooladdRenderer(renderer)- Add a custom rendereraddRecognizer(recognizer)- Add a gesture recognizersetBackground(blob)- Set background from File/BlobsetBackgroundFromUrl(url)- Set background from URLon(event, callback)- Subscribe to engine eventsoff(event, callback)- Unsubscribe from engine events
Zone
Properties
id- Unique identifierrect- Rectangle defining position and sizecolor- Fill colorname- Optional display nameshowName- Whether to display the namenameAnchor- Position of the name labelzIndex- Rendering ordervisible- Visibility flagselectable- Whether the zone can be selectedmovable- Whether the zone can be movedresizable- Whether the zone can be resized
TypeScript Support
Canvas Engine is written in TypeScript and provides full type definitions out of the box.
License
MIT
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
Dependencies
- dytools-geometry - Geometry utilities (Rectangle, Point, Vector, Polygon, Size)
