This is a React-based drawing application built with Next.js, TypeScript, and Konva.js. The application provides a comprehensive set of drawing tools including brushes, shapes, text, and advanced annotation management with selection, resizing, and editing capabilities.
- Frontend: React 18 with Next.js 15
- Canvas Rendering: Konva.js with react-konva
- Language: TypeScript
- Styling: Tailwind CSS
- State Management: React hooks with custom state management
src/
├── app/ # Next.js app router
│ ├── globals.css # Global styles
│ ├── layout.tsx # Root layout
│ └── page.tsx # Main application page
├── components/ # React components
│ ├── Canvas.tsx # Main canvas component with Konva rendering
│ ├── DrawingApp.tsx # Main application component
│ ├── TextInput.tsx # Text input overlay component
│ └── Toolbar.tsx # Drawing tools toolbar
├── hooks/ # Custom React hooks
│ └── useDrawingState.ts # Main state management hook
├── types/ # TypeScript type definitions
│ └── annotations.ts # Annotation and drawing types
└── utils/ # Utility functions
└── geometry.ts # Geometric calculations and hit detection
The main application component that orchestrates all drawing functionality.
Key Responsibilities:
- Tool selection and management
- Mouse event handling (down, move, up)
- Annotation lifecycle management
- Keyboard shortcuts
- Drawing state coordination
Key Features:
- Multi-tool support (brush, shapes, text, select, eraser)
- Real-time drawing feedback
- Selection and multi-selection
- Drag and drop functionality
- Resize operations
- History management (undo/redo)
The rendering component using Konva.js for high-performance canvas operations.
Key Responsibilities:
- Konva Stage and Layer management
- Annotation rendering
- Bounding box and anchor point rendering
- Control point visualization
- Interactive element positioning
Rendering Features:
- Real-time annotation display
- Selection highlighting
- Bounding box visualization
- Resize anchor points
- Control point editing
- Preview modes for drawing tools
The core state management hook containing all drawing logic.
State Management:
- Annotation storage and manipulation
- Drawing tool states
- Selection and editing states
- History management
- Resize and drag operations
Key Functions:
calculateBounds(): Dynamic bounding box calculationresizeAnnotation(): Rate-based resize with inverted resizing supportgetHoveredAnchorIndex(): Anchor point detectioncreateAnnotation(): Annotation creation with proper initialization
The application supports multiple annotation types through a unified interface:
interface BaseAnnotation {
id: string;
type: 'stroke' | 'text' | 'image' | 'shape';
color: string;
strokeWidth: number;
isEditing: boolean;
isSelected: boolean;
}
interface StrokeAnnotation extends BaseAnnotation {
type: 'stroke';
points: number[]; // Flat array of [x, y, x, y, ...]
}
interface ShapeAnnotation extends BaseAnnotation {
type: 'shape';
shapeType: 'rectangle' | 'ellipse' | 'polygon' | 'polyline' | 'line' | 'bezier';
x: number;
y: number;
width: number;
height: number;
points: number[];
}
interface TextAnnotation extends BaseAnnotation {
type: 'text';
text: string;
x: number;
y: number;
fontSize: number;
width: number;
height: number;
}interface ResizeState {
isResizing: boolean;
startPoint: Point | null;
currentPoint: Point | null;
anchorIndex: number | null;
originalAnnotation: Annotation | null; // Deep copy for accurate resizing
}
interface DragState {
isDragging: boolean;
startPoint: Point | null;
offset: Point;
}- Type: Freehand drawing
- Data: Array of points
[x1, y1, x2, y2, ...] - Features: Smooth curves with tension, variable stroke width
- Rectangle: Drag-to-create rectangles
- Ellipse: Drag-to-create ellipses
- Line: Click-to-click or drag-to-create lines
- Polygon: Multi-click polygon creation
- Polyline: Multi-click polyline creation
- Bezier: Three-point bezier curves
- Creation: Drag to create text area
- Editing: Double-click to edit inline
- Features: Auto-resize, font size control
- Selection: Click to select, Ctrl+click for multi-select
- Bounding Box: Visual selection indicator
- Resize: 8-point anchor system with rate-based scaling
- Drag: Move selected annotations
- Control Points: Edit individual points for line-based shapes
- Functionality: Click to delete annotations
- Detection: Geometric intersection with eraser circle
The application implements a sophisticated resize system that supports inverted resizing:
// Calculate rates relative to unchanged anchor point
widthRate = (unchangedX - newX) / originalBounds.width;
heightRate = (unchangedY - newY) / originalBounds.height;
// Apply rate-based transformation
new_px = unchangedX + (old_px - unchangedX) * widthRate;
new_py = unchangedY + (old_py - unchangedY) * heightRate;Benefits:
- Accurate scaling relative to original state
- Support for inverted resizing (negative rates)
- Consistent behavior across all annotation types
- Real-time updates without stale state
Instead of storing bounds as properties, the application calculates them dynamically:
export const calculateBounds = (annotation: Annotation): Bounds => {
switch (annotation.type) {
case 'stroke': {
const xs = points.filter((_, i) => i % 2 === 0);
const ys = points.filter((_, i) => i % 2 === 1);
return {
x: Math.min(...xs),
y: Math.min(...ys),
width: Math.max(...xs) - Math.min(...xs),
height: Math.max(...ys) - Math.min(...ys)
};
}
// ... other types
}
};Advantages:
- Always accurate bounds
- No synchronization issues
- Cleaner data structures
- Better performance
Line-based shapes support individual control point editing:
- Detection: Hover and click detection for control points
- Visual Feedback: Highlighted control points during editing
- Real-time Updates: Live preview during dragging
- Bounds Recalculation: Automatic bounds update after editing
-
Mouse Down: Tool-specific initialization
- Start drawing (brush, shapes)
- Begin selection or resize
- Initiate drag operations
-
Mouse Move: Real-time feedback
- Drawing preview
- Resize updates
- Drag operations
- Hover detection
-
Mouse Up: Completion
- Finalize drawings
- Complete resize operations
- Save to history
- Ctrl/Cmd + Z: Undo
- Ctrl/Cmd + Y: Redo
- Ctrl/Cmd + Shift + Z: Redo (alternative)
- Delete: Delete selected annotations
- Ctrl/Cmd + D: Duplicate selected annotations
- Single Selection: Click on annotation
- Multi-Selection: Ctrl/Cmd + click
- Area Selection: Drag to create selection rectangle
- Clear Selection: Click on empty area
- Layer Management: Separate layers for different rendering contexts
- Event Delegation: Efficient event handling
- Conditional Rendering: Only render necessary elements
- Memoization: useCallback and useMemo for expensive operations
- Immutable Updates: Proper state mutation patterns
- Batch Updates: Group related state changes
- History Management: Efficient undo/redo system
- Deep Copy: JSON-based deep copying for resize operations
Canvas.tsx (826 lines)
- Konva Stage setup and configuration
- Annotation rendering logic
- Interactive element positioning
- Real-time visual feedback
DrawingApp.tsx (939 lines)
- Main application logic
- Event handling coordination
- Tool state management
- Drawing workflow orchestration
Toolbar.tsx
- Tool selection interface
- Color and stroke width controls
- Shape type selection
- Tool state visualization
TextInput.tsx
- HTML overlay for text editing
- Inline text editing functionality
- Keyboard event handling
useDrawingState.ts (453 lines)
- Complete state management
- Annotation CRUD operations
- Resize and drag logic
- History management
- Geometric calculations
annotations.ts (121 lines)
- Complete type definitions
- Interface hierarchies
- State type definitions
- Tool and shape enumerations
geometry.ts (278 lines)
- Hit detection algorithms
- Distance calculations
- Intersection testing
- Geometric utilities
- Define Tool Type: Add to
Toolenum inannotations.ts - Implement Logic: Add handling in
DrawingApp.tsxmouse events - Add UI: Update
Toolbar.tsxwith new tool button - Update State: Modify
useDrawingState.tsif needed
- Define Interface: Extend
BaseAnnotationinannotations.ts - Update Calculations: Modify
calculateBounds()function - Add Rendering: Update
Canvas.tsxrender logic - Handle Interactions: Update selection and editing logic
- Use
useCallbackfor event handlers - Implement proper memoization for expensive calculations
- Minimize re-renders with careful state management
- Use Konva's built-in optimization features
The application can be tested by:
- Running
npm run dev - Testing all drawing tools
- Verifying selection and resize functionality
- Testing keyboard shortcuts
- Checking undo/redo operations
Potential areas for improvement:
- Layer management system
- Advanced shape tools (freeform, star, etc.)
- Image import and manipulation
- Collaborative editing
- Export functionality (SVG, PNG, PDF)
- Advanced text formatting
- Custom brush types
- Animation support