Skip to content

Latest commit

 

History

History
566 lines (462 loc) · 17.7 KB

File metadata and controls

566 lines (462 loc) · 17.7 KB

Play Canvas Architecture Documentation

Overview

The Play Canvas is a professional football play diagramming tool built with React and Konva.js. It provides coaches with an infinite canvas for creating, editing, and managing football play diagrams with a component-based reusable system.

Location: /src/components/PlayCanvas.tsx
Type System: /src/types/visualPlaybook.ts
Dependencies: React, Konva, react-konva, Lucide React icons

Core Architecture

1. Technology Stack

  • Konva.js: 2D canvas library providing the rendering engine
  • react-konva: React wrapper for Konva.js enabling declarative canvas elements
  • TypeScript: Full type safety for all canvas elements and interactions
  • Lucide React: Icon library for tool buttons and UI elements

2. Component Structure

PlayCanvas Component (Main)
├── State Management (useState hooks)
├── Event Handlers
├── UI Components
   ├── Top Toolbar
      ├── Undo/Redo Controls
      ├── Component Management
      ├── View Controls (Grid, Snap)
      ├── Zoom Controls
      └── Actions (Copy, Delete, Export)
   ├── Side Toolbar
      ├── Selection Tool
      ├── Shape Tools (Square, Circle, Text)
      └── Line Tools (Straight, Arrow, Curved)
   └── Canvas Area
       ├── Konva Stage
       ├── Konva Layer
       ├── Grid System
       ├── Selection Rectangle
       ├── Canvas Elements
       └── Transformer
├── Floating Components
   ├── Component List
   └── Help Text
└── Helper Functions

State Management

Core State Variables

// Tool Selection
const [tool, setTool] = useState<Tool>('select');
// Tools: 'select' | 'pan' | 'square' | 'circle' | 'text' | 'line' | 'arrow' | 'curved'

// Canvas Elements
const [elements, setElements] = useState<CanvasElement[]>([]);
// Stores all drawn elements on the canvas

// Selection System
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const [isSelecting, setIsSelecting] = useState(false);
const [selectionRect, setSelectionRect] = useState({
  x: 0, y: 0, width: 0, height: 0, startX: 0, startY: 0
});

// Component System
const [components, setComponents] = useState<PlaybookComponent[]>([]);
// Saved reusable component groups

// Canvas View
const [scale, setScale] = useState(1); // Zoom level
const [stagePos, setStagePos] = useState({ x: 400, y: 50 }); // Canvas position (updated for field view)

// Grid and Field System
const [showGrid, setShowGrid] = useState(true);
const [snapToGrid, setSnapToGrid] = useState(false);
const [showField, setShowField] = useState(false); // NEW: NCAA football field overlay
const [fieldScale, setFieldScale] = useState(2.5); // NEW: Field size multiplier
const gridSize = 20; // Fixed 20px grid

// Drawing State
const [isDrawing, setIsDrawing] = useState(false);
const [linePoints, setLinePoints] = useState<number[]>([]);
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });

// History System
const [history, setHistory] = useState<CanvasElement[][]>([[]]);
const [historyIndex, setHistoryIndex] = useState(0);

// Keyboard Modifiers
const [isShiftPressed, setIsShiftPressed] = useState(false);
const [isSpacePressed, setIsSpacePressed] = useState(false);

// Clipboard
const [copiedElements, setCopiedElements] = useState<CanvasElement[]>([]);

Data Types

CanvasElement Interface

interface CanvasElement {
  id: string;                    // Unique identifier
  type: ElementType;              // 'square' | 'circle' | 'text' | 'line' | 'arrow' | 'curved'
  x: number;                      // X position
  y: number;                      // Y position
  
  // Shape-specific properties
  width?: number;                 // For squares
  height?: number;                // For squares
  radius?: number;                // For circles
  text?: string;                  // For text elements
  fontSize?: number;              // For text
  fontFamily?: string;            // For text
  fontStyle?: string;             // For text
  
  // Line-specific properties
  points?: number[];              // Array of [x1, y1, x2, y2, ...] for lines
  tension?: number;               // Curve tension for curved lines
  pointerLength?: number;         // Arrow head length
  pointerWidth?: number;          // Arrow head width
  
  // Styling
  fill?: string;                  // Fill color
  stroke?: string;                // Stroke color
  strokeWidth?: number;           // Stroke width
  cornerRadius?: number;          // Rounded corners for squares
  rotation?: number;              // Rotation in degrees
  
  // Visual effects
  dash?: number[];                // Dashed line pattern
  opacity?: number;               // Element opacity
  shadowColor?: string;           // Shadow color
  shadowBlur?: number;            // Shadow blur radius
  shadowOpacity?: number;         // Shadow opacity
}

PlaybookComponent Interface

interface PlaybookComponent {
  id: string;                     // Unique identifier
  name: string;                   // Component name (uppercase)
  elements: CanvasElement[];      // Array of elements in component
  createdAt: Date;                // Creation timestamp
  updatedAt: Date;                // Last update timestamp
}

Event Handling System

Mouse Events

handleStageMouseDown

  • Purpose: Initiates selection rectangle, element placement, or line drawing
  • Behavior:
    • In select mode + empty canvas: Starts selection rectangle
    • In line tools + empty canvas: NEW Starts line drawing (click-and-drag)
    • Prevents canvas dragging while selecting
    • Sets up selection rectangle coordinates or line start point

handleStageClick

  • Purpose: Places new elements on canvas
  • Behavior:
    • Only fires when not in select mode
    • Places shapes/text at click position
    • Handles line drawing (start/end points)
    • Respects snap-to-grid if enabled

handleMouseMove

  • Purpose: Updates selection rectangle and line preview
  • Behavior:
    • Updates selection rectangle dimensions
    • Calculates element intersections
    • Shows line preview while drawing
    • Handles Shift constraint for 45° angles

handleMouseUp

  • Purpose: Completes selection rectangle or line drawing
  • Behavior:
    • Finalizes multi-select operation
    • NEW Completes line drawing (click-and-drag behavior)
    • Applies minimum length check for lines (10px minimum)
    • Elements remain selected

Keyboard Events

Key Bindings

Select All:     Ctrl+A
Copy:           Ctrl+C
Paste:          Ctrl+V
Duplicate:      Ctrl+D
Undo:           Ctrl+Z
Redo:           Ctrl+Y
Delete:         Delete key
Pan Canvas:     Space (hold) + drag
Constrain:      Shift (hold) - constrains lines to 45° angles
Cancel:         Escape - cancels current operation

Canvas Navigation

Zoom System

  • Mouse Wheel: Zoom in/out centered on cursor
  • Zoom Buttons: Fixed percentage zoom
  • Scale Range: 10% to 500%
  • Algorithm: Maintains cursor position during zoom

Pan System

  • Pan Tool: NEW Dedicated hand tool for canvas navigation
  • Space + Drag: Pan the canvas view (legacy method)
  • Visual Feedback: Cursor changes to grab hand
  • State: Uses isSpacePressed flag or tool === 'pan'
  • Stage Draggable: Enabled when pan tool is active or space is pressed

Tool System

1. Selection Tool (select)

  • Multi-select: Drag rectangle to select multiple
  • Single select: Click individual elements
  • Add to selection: Shift+click
  • Move elements: Drag selected elements
  • Pan canvas: Space + drag

2. Pan Tool (pan) - NEW

  • Purpose: Navigate the infinite canvas
  • Interaction: Click and drag to move view
  • Cursor: Shows grab hand
  • Alternative: Space + drag in select mode

3. Shape Tools

Square Tool (square)

  • Purpose: Linemen (O-Line/D-Line)
  • Default: 40x40px, white fill, black stroke
  • Features: Rounded corners (4px radius)

Circle Tool (circle)

  • Purpose: Skill position players
  • Default: 20px radius, white fill, black stroke
  • Features: Perfect circles for players

Text Tool (text)

  • Purpose: Position labels (QB, RB, WR, etc.)
  • Default: 20px font, bold, system font
  • Input: Prompt dialog for text entry

4. Line Tools

Straight Line (line)

  • Purpose: Basic routes and blocking
  • Interaction: UPDATED Click and drag (not click-start, click-end)
  • Shift Constraint: 45° angle snapping
  • Minimum Length: 10px to prevent accidental dots

Arrow Line (arrow)

  • Purpose: Pass routes with direction
  • Features: Arrow head (10px)
  • Width: 3px stroke
  • Interaction: UPDATED Click and drag
  • Preview: Shows dashed arrow while dragging

Curved Line (curved)

  • Purpose: Motion and complex routes
  • Tension: 0.5 for smooth curves
  • Interaction: UPDATED Click and drag
  • Auto-Curve: NEW Curves perpendicular to drag direction (30px offset)
  • Preview: Shows curved preview while dragging

Component System

Saving Components

  1. Select multiple elements (drag selection)
  2. Click "Save" button
  3. Enter component name (auto-uppercase)
  4. Elements saved with relative positioning
  5. Toast notification confirms save
  6. Stored in localStorage

Loading Components

  1. Type component name(s) in input
  2. Click "Load" or press Enter
  3. Multiple components space-separated
  4. Places at offset position (100, 100)
  5. Automatically selected after load

Component Storage

  • Location: Browser localStorage
  • Key: 'playbookComponents'
  • Format: JSON stringified array
  • Persistence: Survives page refresh

Grid & Field System

Grid Display

  • Size: 20px squares
  • Color: #e5e7eb (light gray)
  • Toggle: Grid button in toolbar
  • Performance: Non-interactive (listening: false)

Snap to Grid

  • Toggle: Lock/Unlock button
  • Behavior: Rounds to nearest 20px
  • Applies to: All placement and movement
  • Visual: Lock icon when enabled

NCAA Football Field - NEW

  • Toggle: Green football icon (Activity) in toolbar
  • Orientation: Vertical (end zones at top/bottom)
  • Dimensions:
    • Width: 53⅓ yards (NCAA regulation)
    • Length: 120 yards (100 field + 2×10 end zones)
    • Scale: Adjustable 1.0x to 5.0x via slider
  • Components:
    • Field boundary with light gray stroke
    • End zones with light shading
    • Yard lines every 5 yards (thicker every 10)
    • Yard numbers (10, 20, 30, 40, 50)
    • Hash marks every yard at NCAA positions (20 yards from sidelines)
  • Auto-Zoom: Automatically zooms out to 50% when field is enabled
  • Field Scale Slider: Appears when field is active for size adjustment
  • Use Case: Provides realistic proportions for play diagramming

Selection System

Rectangle Selection

  1. Initiation: Mouse down on empty canvas
  2. Visual: Blue dashed rectangle with semi-transparent fill
  3. Calculation: Intersection detection for all elements
  4. Completion: Mouse up finalizes selection

Element Bounds Calculation

getElementBounds(element) {
  switch(type):
    'square': { x, y, width, height }
    'circle': { x: centerX - radius, y: centerY - radius, width: radius*2, height: radius*2 }
    'text': { x, y, width: ~100px, height: fontSize }
    'line/arrow/curved': { bounding box of all points }
}

Intersection Detection

  • Algorithm: AABB (Axis-Aligned Bounding Box)
  • Efficiency: O(n) for n elements
  • Updates: Real-time during drag

History System (Undo/Redo)

Implementation

  • Structure: Array of canvas states
  • Index: Current position in history
  • Save Points: After each modification
  • Limit: Unbounded (consider adding max)

Operations

undo(): Moves to previous state
redo(): Moves to next state
saveToHistory(): Adds current state

Export System

PNG Export

  1. Uses Konva's toDataURL() method
  2. Creates download link
  3. Auto-downloads as 'playbook.png'
  4. Includes all visible elements

Performance Optimizations

Rendering

  • Batching: Transformer batch updates
  • Listening: Grid lines non-interactive
  • Preview elements: Non-interactive during draw

State Management

  • Selective updates: Only modified elements
  • ID generation: Timestamp + random for uniqueness
  • Local operations: No server calls

Visual Feedback System

Selection Indicators

  • Selected elements: Blue stroke (#0066ff)
  • Increased stroke width: 3px when selected
  • Shadow effect: Blue shadow on selection

Tool Indicators

  • Active tool: Blue background in toolbar
  • Cursor changes: Based on current tool/mode
  • Help text: Context-sensitive instructions

Drawing Previews

  • Lines: Dashed preview while drawing
  • Color: Gray (#666666)
  • Updates: Follow mouse position

Critical Implementation Details

Coordinate System

  • Origin: Top-left (0, 0)
  • Relative positioning: Uses getRelativePointerPosition()
  • Fixes shape jumping: Accurate coordinate calculation
  • Canvas stability: No auto-centering

Tool Persistence

  • No auto-switch: Tools stay selected
  • Rapid placement: Multiple shapes without interruption
  • Clean state: Tool switches clear in-progress operations

Canvas Stability

  • Initial offset: (100, 100) for better workspace
  • Pan control: Only with Space+drag
  • No view snapping: Canvas stays where positioned

localStorage Schema

Components Storage

{
  "playbookComponents": [
    {
      "id": "element_123456_abc",
      "name": "TRIPS RIGHT",
      "elements": [...],
      "createdAt": "2024-01-01T00:00:00.000Z",
      "updatedAt": "2024-01-01T00:00:00.000Z"
    }
  ]
}

Integration Points

With Main App

  • Route: Accessible via palette icon in navigation
  • Component: Standalone, no external dependencies
  • State: Self-contained, no global state

Future Integration Opportunities

  1. Play Library: Save diagrams with play calls
  2. Practice Scripts: Attach diagrams to plays
  3. Export Options: SVG, PDF formats
  4. Collaboration: Real-time multi-user editing
  5. Animation: Play progression/motion

Common Customization Points

Adding New Tools

  1. Add to Tool type in visualPlaybook.ts
  2. Add tool button in side toolbar
  3. Add handler in handleStageClick
  4. Add element rendering in render section
  5. Add help text for tool

Adding New Element Properties

  1. Update CanvasElement interface
  2. Add property handling in element creation
  3. Update rendering to use property
  4. Consider bounds calculation impact

Modifying Grid System

  • Grid size: Change gridSize constant
  • Grid color: Modify stroke in grid rendering
  • Snap behavior: Adjust snapValue function

Troubleshooting Guide

Common Issues

  1. Shapes jumping on placement

    • Check coordinate calculation
    • Verify getRelativePointerPosition() usage
    • Ensure no stage position updates
  2. Selection not working

    • Verify isSelecting state
    • Check mouse event handlers
    • Ensure proper event propagation
  3. Canvas moving unexpectedly

    • Check draggable prop conditions
    • Verify isSpacePressed logic
    • Review stage position updates
  4. Tools not switching

    • Check tool button onClick handlers
    • Verify state cleanup on switch
    • Ensure no blocking conditions

Best Practices

  1. Always use getRelativePointerPosition() for accurate coordinates
  2. Clear in-progress operations when switching tools
  3. Provide visual feedback for all interactions
  4. Save to history after modifications
  5. Use non-interactive elements for previews/guides
  6. Maintain tool state for rapid workflows
  7. Test with multiple elements for performance

Development Workflow

Adding Features

  1. Update types in visualPlaybook.ts
  2. Add state variables if needed
  3. Implement event handlers
  4. Add UI controls
  5. Update rendering logic
  6. Add help text
  7. Test all interactions

Testing Checklist

  • Multi-select works correctly
  • Snap to grid functions properly
  • Undo/redo maintains consistency
  • Components save/load correctly
  • Export produces valid images
  • Keyboard shortcuts work
  • Touch support functions
  • Performance with 100+ elements

Recent Updates (January 2025)

v1.1.0 - Navigation and Field Enhancements

  • Pan Tool: Dedicated hand tool for canvas navigation
  • NCAA Football Field: Regulation field overlay with adjustable scaling
  • Click-and-Drag Lines: Updated all line tools to use click-and-drag interaction
  • Auto-Curve System: Curved lines automatically bend perpendicular to drag direction
  • Field Scale Control: Dynamic field sizing with 1.0x-5.0x range
  • Improved UX: Better initial positioning and auto-zoom for field view

Future Enhancements Roadmap

Phase 1: Enhanced Drawing

  • Freehand drawing tool
  • Shape library (triangles, polygons)
  • Text editing in-place
  • Line style options (dotted, thick)
  • Copy/paste between field and non-field views

Phase 2: Advanced Features

  • Layers system
  • Grouping without components
  • Alignment tools
  • Measurement tools
  • Copy formatting

Phase 3: Integration

  • Connect to Play Library
  • Template system
  • Import from image
  • Video export for animations
  • Collaborative editing

Contact & Maintenance

This component is a core part of the Football Play/Roster Manager system. For questions or issues, refer to the main CLAUDE.md documentation.

Last Updated: January 2025 Version: 1.1.0 - Navigation and Field Enhancements