React Annotation Tool
This challenge asks you to build a basic annotation tool using React and TypeScript. The tool will allow users to draw rectangles on an image and save the coordinates of those rectangles. This is a common feature in image editing applications, data labeling platforms, and other visual tools, making it a valuable exercise in component design and state management.
Problem Description
You are tasked with creating a React component that displays an image and allows users to draw rectangular annotations on it. The component should:
- Display an Image: Render an image from a provided URL.
- Drawing Functionality: Allow users to click and drag to create rectangular annotations. The rectangle should be visually displayed on the image as the user drags.
- State Management: Store the coordinates (x1, y1, x2, y2) of the currently drawn rectangle in the component's state. If no rectangle is being drawn, the state should be null.
- Clear Annotation: Provide a button to clear the current annotation, resetting the state to null.
- Save Annotation (Placeholder): Include a button labeled "Save" that, for this challenge, simply logs the annotation coordinates to the console when clicked. (Full saving functionality is beyond the scope of this exercise).
- Error Handling: Display an error message if the image URL is invalid or if the image fails to load.
Expected Behavior:
- When the user clicks and drags, a rectangle should appear, dynamically resizing based on the mouse position.
- The rectangle's position should be updated in real-time as the user drags.
- Clicking the "Clear" button should remove the rectangle and reset the state.
- Clicking the "Save" button should log the rectangle coordinates to the console.
- If the image fails to load, an appropriate error message should be displayed.
Edge Cases to Consider:
- Invalid Image URL: Handle cases where the provided image URL is incorrect or inaccessible.
- Zero-Sized Rectangle: Ensure the rectangle coordinates are valid (x1 < x2 and y1 < y2). You can either prevent the user from creating such rectangles or handle them gracefully.
- Dragging Outside Image Boundaries: Consider how the rectangle should behave if the user drags it outside the image boundaries. Clipping to the image boundaries is a reasonable approach.
- Performance: While not a primary concern for this basic tool, be mindful of potential performance issues if the image is very large.
Examples
Example 1:
Input: imageURL = "https://via.placeholder.com/600x400", initialAnnotation = null
Output: A React component displaying the placeholder image with a rectangle drawn from (100, 50) to (300, 200) as the user drags the mouse.
Explanation: The user clicked at (100, 50) and dragged to (300, 200), creating a rectangle with those coordinates.
Example 2:
Input: imageURL = "invalid_url", initialAnnotation = null
Output: A React component displaying an error message: "Error: Could not load image."
Explanation: The image URL is invalid, so the component displays an error message instead of the image.
Example 3:
Input: imageURL = "https://via.placeholder.com/600x400", initialAnnotation = {x1: 50, y1: 25, x2: 150, y2: 125}
Output: A React component displaying the placeholder image with a rectangle already drawn from (50, 25) to (150, 125).
Explanation: The component initializes with a pre-defined annotation.
Constraints
- Image Size: The image can be up to 2000x2000 pixels.
- Coordinate Range: Rectangle coordinates should be within the bounds of the image.
- Dependencies: You are allowed to use standard React and TypeScript features. External libraries for drawing are not permitted for this challenge. Focus on core React concepts.
- Performance: The component should remain responsive during dragging operations. Avoid unnecessary re-renders.
Notes
- Consider using
useStatefor managing the annotation coordinates. - Use event listeners (e.g.,
onMouseDown,onMouseMove,onMouseUp) to handle the drawing logic. - Think about how to efficiently update the rectangle's position as the user drags the mouse.
- You can use CSS to style the rectangle.
- The "Save" button is a placeholder; you don't need to implement actual saving functionality. Simply log the coordinates to the console.
- Focus on creating a functional and visually clear annotation tool. Clean code and good component structure are important.