React Whiteboard Canvas: Collaborative Drawing
This challenge asks you to build a functional whiteboard canvas component in React using TypeScript. This is a fundamental building block for many collaborative applications, from real-time brainstorming tools to educational platforms. You'll need to handle user input for drawing, manage the canvas state, and render the drawings.
Problem Description
Your task is to create a React component that acts as a digital whiteboard. Users should be able to draw on this canvas using their mouse.
Key Requirements:
- Drawing Functionality:
- Users can click and drag their mouse to draw lines on the canvas.
- The drawing should be a continuous line, not a series of disconnected dots.
- The drawn lines should have a consistent color and thickness.
- Canvas State Management:
- The component must maintain the state of all drawings made on the canvas.
- When the component re-renders, all previous drawings should still be visible.
- Rendering:
- The drawing should be rendered onto an HTML
<canvas>element. - The canvas should be resizable (within reasonable bounds, no need for complex dynamic resizing logic for this challenge).
- The drawing should be rendered onto an HTML
- User Interface (Basic):
- A clear area representing the whiteboard canvas.
- (Optional, but recommended for a more complete experience): Basic controls to clear the canvas and perhaps change the drawing color or thickness.
Expected Behavior:
- When the user presses down the mouse button on the canvas, a new drawing stroke begins.
- As the user moves the mouse while the button is held down, the stroke is extended.
- When the user releases the mouse button, the stroke is finalized.
- All finalized strokes persist on the canvas.
- If a "clear" button is implemented, clicking it should remove all drawings from the canvas.
Edge Cases to Consider:
- Rapid Mouse Movements: The drawing should still appear smooth even with fast mouse movements.
- Starting/Ending Drawing Outside Canvas: How does the component behave if the user starts or ends a drag outside the canvas boundaries? (For this challenge, assume drawing only happens when the mouse is down and within the canvas).
- No Drawing: What happens if the user clicks without dragging? (Should ideally not draw anything).
Examples
Example 1: Basic Drawing
Input: User clicks down on the canvas at (10, 20), drags to (30, 40), and releases.
Output: A line segment is drawn on the canvas connecting (10, 20) to (30, 40).
Explanation: The mouse down event initiates a drawing stroke. The subsequent mouse move events append points to this stroke. The mouse up event finalizes the stroke, rendering it permanently on the canvas.
Example 2: Multiple Strokes
Input:
1. User draws a line from (50, 50) to (150, 100).
2. User lifts the mouse, then clicks down at (200, 150) and draws to (250, 200).
Output: Two distinct line segments are visible on the canvas.
Explanation: Each separate mouse down-move-up sequence creates a new, independent drawing stroke.
Example 3: Clearing the Canvas
Input:
1. User draws several lines.
2. User clicks a "Clear Canvas" button.
Output: The canvas is completely blank.
Explanation: The "Clear Canvas" action resets the internal state of the drawings, effectively removing them all.
Constraints
- The canvas should be a fixed size (e.g., 800px wide by 600px tall) for simplicity.
- The drawing tool should use a default line color (e.g., black) and thickness (e.g., 2px).
- The React component should be built using TypeScript.
- Avoid using external libraries for drawing (e.g.,
react-konva,fabric.js). Rely on the native HTML<canvas>API and React state management. - Performance: Ensure drawing remains reasonably smooth for typical usage.
Notes
- You will need to use the
useRefhook to get access to the<canvas>DOM element. - The
getContext('2d')method of the canvas element will provide the drawing API. - You'll need to manage state for whether the user is currently drawing (e.g.,
isDrawingboolean) and the list of points or paths that constitute the drawings. - Consider how to store drawing data. An array of paths, where each path is an array of points, is a common approach.
- Think about how to handle mouse events (
onMouseDown,onMouseMove,onMouseUp) and translate them into canvas drawing commands. - For rendering, you might draw all stored paths onto the canvas whenever the state changes, or more efficiently, draw new strokes as they are created and then redraw all on state changes.