React Annotation Tool Challenge
This challenge asks you to build a foundational annotation tool using React and TypeScript. Annotation tools are crucial for various applications, from image editing and data labeling to code review and content moderation. Successfully completing this challenge will demonstrate your ability to manage UI state, handle user interactions, and structure a reusable React component.
Problem Description
Your task is to create a React component that allows users to draw rectangular annotations on an image. The component should enable users to:
- Display an Image: Render a provided image within a designated area.
- Draw Rectangles: Allow users to click and drag on the image to draw rectangular annotations.
- Visualize Annotations: Display the drawn rectangles on top of the image.
- Manage Annotations: Maintain a list of all created annotations, including their position and dimensions.
- Basic Interaction: Users should be able to start drawing a new annotation by clicking and dragging, and the annotation should be finalized upon releasing the mouse button.
Key Requirements:
- The component must be built using React and TypeScript.
- Annotations should be represented as rectangles defined by their
x,y(top-left corner),width, andheight. - The drawing process should be intuitive: click and drag to define the rectangle's boundaries.
- Annotations should be visually distinct (e.g., a colored border).
- The component should accept the image source URL as a prop.
- The component should expose a way to access the list of annotations (e.g., via a callback prop).
Expected Behavior:
- When the user clicks and starts dragging, a temporary rectangle might be shown to indicate the drawing in progress.
- Upon releasing the mouse, a permanent annotation should be added to the list and rendered on the image.
- The tool should be responsive to the image's dimensions, and annotations should maintain their relative positions.
Edge Cases:
- Drawing zero-width or zero-height rectangles (consider whether to allow or ignore these).
- Rapidly clicking and releasing without dragging (should not create an annotation).
- Drawing outside the image boundaries (annotations should ideally be clipped or the drawing confined to the image).
Examples
Example 1: Basic Drawing
Input:
imageUrl: "path/to/your/image.jpg"- User clicks at
(x: 50, y: 100)and drags to(x: 150, y: 200)before releasing.
Output:
- The image is displayed.
- A rectangle is rendered on the image with its top-left corner at
(50, 100), width100, and height100. - An annotation object like
{ id: "unique-id-1", x: 50, y: 100, width: 100, height: 100 }is added to the internal state. - A callback
onAnnotationsChangeis called with the updated list of annotations.
Example 2: Multiple Annotations
Input:
imageUrl: "path/to/another/image.png"- User draws the first annotation from
(x: 20, y: 30)to(x: 80, y: 70). - User then draws a second annotation from
(x: 100, y: 120)to(x: 130, y: 180).
Output:
- The image is displayed.
- Two rectangles are rendered on the image.
- The internal state contains two annotation objects.
onAnnotationsChangeis called with an array containing both annotations.
Example 3: Edge Case - Minimal Drag
Input:
imageUrl: "path/to/sample.gif"- User clicks at
(x: 75, y: 75)and immediately releases without significant dragging.
Output:
- The image is displayed.
- No new annotation is added to the list.
onAnnotationsChangeis called with the same list of annotations as before the click.
Constraints
- The annotation tool should be a functional React component.
- The image dimensions should not exceed
1000pxin width or1000pxin height for testing purposes. - The number of annotations should not exceed
50per image. - The component should efficiently re-render only when necessary.
Notes
- Consider using CSS for styling the annotations. Absolute positioning will be key for placing them correctly on the image.
- You'll need to manage several pieces of state: the list of current annotations, the coordinates of the rectangle being drawn (if any), and whether the user is currently drawing.
- Think about how to handle mouse events (
onMouseDown,onMouseMove,onMouseUp) to capture the drawing interaction. - For simplicity, you don't need to implement editing, deletion, or resizing of annotations in this challenge. Focus on the core drawing functionality.
- When calculating width and height from two points, remember that the user might drag from right-to-left or bottom-to-top. Your logic should account for this to ensure
widthandheightare always positive values. - You can use a simple unique ID generator (e.g., a counter or
Date.now()) for annotation IDs.