CanvasMode Enum
Defines the different interaction modes available on the canvas. These modes determine how user input is interpreted and what actions can be performed.
enum CanvasMode {
None,
Pressing,
SelectionNet,
Translating,
Inserting,
Resizing,
Pencil,
}
Default idle state with no active interaction
User has pressed down but hasn’t initiated a specific action yet
Drawing a selection rectangle to select multiple layers
Moving/dragging selected layers across the canvas
Inserting a new layer onto the canvas
Resizing a selected layer by dragging resize handles
Drawing free-form paths with the pencil tool
CanvasState Type
A discriminated union type that represents the current state of the canvas. Each mode can carry additional data specific to that interaction.
type CanvasState =
| { mode: CanvasMode.None }
| { mode: CanvasMode.SelectionNet; origin: Point; current?: Point }
| { mode: CanvasMode.Translating; current: Point }
| {
mode: CanvasMode.Inserting;
layerType:
| LayerType.Ellipse
| LayerType.Rectangle
| LayerType.Text
| LayerType.Note;
}
| { mode: CanvasMode.Pencil }
| { mode: CanvasMode.Pressing; origin: Point }
| { mode: CanvasMode.Resizing; initialBounds: XYWH; corner: Side };
None State
{ mode: CanvasMode.None }
SelectionNet State
{ mode: CanvasMode.SelectionNet; origin: Point; current?: Point }
mode
CanvasMode.SelectionNet
required
Drawing selection rectangle
Starting point where the selection began. See Point type.
Current pointer position while dragging the selection net
Translating State
{ mode: CanvasMode.Translating; current: Point }
mode
CanvasMode.Translating
required
Moving selected layers
Current pointer position during the drag operation. See Point type.
Inserting State
{
mode: CanvasMode.Inserting;
layerType:
| LayerType.Ellipse
| LayerType.Rectangle
| LayerType.Text
| LayerType.Note;
}
mode
CanvasMode.Inserting
required
Inserting a new layer
The type of layer being inserted. Can be Ellipse, Rectangle, Text, or Note. Path layers are created via Pencil mode.
Pencil State
{ mode: CanvasMode.Pencil }
mode
CanvasMode.Pencil
required
Drawing free-form paths
Pressing State
{ mode: CanvasMode.Pressing; origin: Point }
mode
CanvasMode.Pressing
required
Pointer is down but action not yet determined
Resizing State
{ mode: CanvasMode.Resizing; initialBounds: XYWH; corner: Side }
mode
CanvasMode.Resizing
required
Resizing a layer
The layer’s dimensions before resizing began. See XYWH type.
Which corner or edge is being dragged. See Side enum.
Usage Examples
State Management
import { useState } from 'react';
import { CanvasMode, CanvasState } from '@/types/canvas';
const [canvasState, setCanvasState] = useState<CanvasState>({
mode: CanvasMode.None,
});
Handling Pointer Events
import { CanvasMode, Point } from '@/types/canvas';
function onPointerDown(e: React.PointerEvent) {
const point: Point = pointerEventToCanvasPoint(e, camera);
if (canvasState.mode === CanvasMode.Inserting) {
return;
}
setCanvasState({
mode: CanvasMode.Pressing,
origin: point
});
}
Mode Transitions
import { CanvasMode, CanvasState } from '@/types/canvas';
function onPointerMove(current: Point) {
if (canvasState.mode === CanvasMode.Pressing) {
// Transition to SelectionNet if movement detected
startMultiSelection(current, canvasState.origin);
} else if (canvasState.mode === CanvasMode.SelectionNet) {
updateSelectionNet(current, canvasState.origin);
} else if (canvasState.mode === CanvasMode.Translating) {
translateSelectedLayers(current);
} else if (canvasState.mode === CanvasMode.Resizing) {
resizeSelectedLayer(current);
}
}
Inserting Layers
import { CanvasMode, LayerType } from '@/types/canvas';
function startInsertingRectangle() {
setCanvasState({
mode: CanvasMode.Inserting,
layerType: LayerType.Rectangle,
});
}
function onPointerUp(e: React.PointerEvent) {
const point = pointerEventToCanvasPoint(e, camera);
if (canvasState.mode === CanvasMode.Inserting) {
insertLayer(canvasState.layerType, point);
} else {
setCanvasState({ mode: CanvasMode.None });
}
}
Resizing with Initial Bounds
import { CanvasMode, XYWH, Side } from '@/types/canvas';
function onResizeHandlePointerDown(corner: Side, initialBounds: XYWH) {
history.pause();
setCanvasState({
mode: CanvasMode.Resizing,
initialBounds,
corner,
});
}
Type Guards
import { CanvasState, CanvasMode } from '@/types/canvas';
function isTranslating(state: CanvasState): state is { mode: CanvasMode.Translating; current: Point } {
return state.mode === CanvasMode.Translating;
}
function isInserting(state: CanvasState): state is { mode: CanvasMode.Inserting; layerType: LayerType } {
return state.mode === CanvasMode.Inserting;
}