Color Type
Represents an RGB color value used throughout the canvas for fills, strokes, and other color properties.
type Color = {
r: number;
g: number;
b: number;
};
Red channel value (0-255)
Green channel value (0-255)
Blue channel value (0-255)
Usage Example
import { Color } from '@/types/canvas';
const redColor: Color = { r: 255, g: 0, b: 0 };
const greenColor: Color = { r: 0, g: 255, b: 0 };
const customColor: Color = { r: 123, g: 45, b: 67 };
// Convert to CSS color string
const cssColor = `rgb(${redColor.r},${redColor.g},${redColor.b})`;
Point Type
Represents a 2D coordinate point on the canvas.
type Point = {
x: number;
y: number;
};
Usage Example
import { Point } from '@/types/canvas';
const origin: Point = { x: 0, y: 0 };
const cursorPosition: Point = { x: 150, y: 200 };
// Calculate distance between points
function distance(p1: Point, p2: Point): number {
return Math.sqrt(
Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)
);
}
Camera Type
Represents the viewport’s pan offset on the canvas. Used to implement pan/zoom functionality.
type Camera = {
x: number;
y: number;
};
Horizontal pan offset in pixels
Vertical pan offset in pixels
Usage Example
import { Camera } from '@/types/canvas';
import { useState } from 'react';
const [camera, setCamera] = useState<Camera>({ x: 0, y: 0 });
function onWheel(e: React.WheelEvent) {
setCamera((camera) => ({
x: camera.x - e.deltaX,
y: camera.y - e.deltaY,
}));
}
// Apply camera transform to SVG group
<g style={{ transform: `translate(${camera.x}px, ${camera.y}px)` }}>
{/* Canvas content */}
</g>
XYWH Type
Represents a rectangular bounding box with position and dimensions.
type XYWH = {
x: number;
y: number;
width: number;
height: number;
};
X coordinate of the top-left corner
Y coordinate of the top-left corner
Usage Example
import { XYWH } from '@/types/canvas';
const bounds: XYWH = {
x: 100,
y: 150,
width: 200,
height: 100,
};
// Check if point is inside bounds
function isPointInBounds(point: Point, bounds: XYWH): boolean {
return (
point.x >= bounds.x &&
point.x <= bounds.x + bounds.width &&
point.y >= bounds.y &&
point.y <= bounds.y + bounds.height
);
}
Side Enum
Represents the edges and corners of a bounding box, used for resize operations. Values are bit flags that can be combined.
enum Side {
Top = 1,
Bottom = 2,
Left = 4,
Right = 8,
}
Bottom edge (binary: 0010)
Right edge (binary: 1000)
Combining Sides
Since Side uses bit flags, you can combine values to represent corners:
import { Side } from '@/types/canvas';
const topLeft = Side.Top | Side.Left; // 5 (0101)
const topRight = Side.Top | Side.Right; // 9 (1001)
const bottomLeft = Side.Bottom | Side.Left; // 6 (0110)
const bottomRight = Side.Bottom | Side.Right; // 10 (1010)
Usage Example
import { Side, XYWH } from '@/types/canvas';
function onResizeHandlePointerDown(corner: Side, initialBounds: XYWH) {
setCanvasState({
mode: CanvasMode.Resizing,
initialBounds,
corner,
});
}
// Check which sides are being resized
function isResizingTop(corner: Side): boolean {
return (corner & Side.Top) !== 0;
}
function isResizingLeft(corner: Side): boolean {
return (corner & Side.Left) !== 0;
}
Common Patterns
Converting Pointer Events to Canvas Points
import { Point, Camera } from '@/types/canvas';
function pointerEventToCanvasPoint(
e: React.PointerEvent,
camera: Camera
): Point {
return {
x: e.clientX - camera.x,
y: e.clientY - camera.y,
};
}
Color State Management
import { useState } from 'react';
import { Color } from '@/types/canvas';
const [lastUsedColor, setLastUsedColor] = useState<Color>({
r: 255,
g: 0,
b: 0,
});
Boundary Calculations
import { XYWH, Point } from '@/types/canvas';
function getBoundsFromPoints(start: Point, end: Point): XYWH {
return {
x: Math.min(start.x, end.x),
y: Math.min(start.y, end.y),
width: Math.abs(end.x - start.x),
height: Math.abs(end.y - start.y),
};
}
Selection Net Rendering
import { CanvasMode } from '@/types/canvas';
{canvasState.mode === CanvasMode.SelectionNet &&
canvasState.current != null && (
<rect
className="fill-blue-500/5 stroke-blue-500 stroke-1"
x={Math.min(canvasState.origin.x, canvasState.current.x)}
y={Math.min(canvasState.origin.y, canvasState.current.y)}
width={Math.abs(canvasState.origin.x - canvasState.current.x)}
height={Math.abs(canvasState.origin.y - canvasState.current.y)}
/>
)
}