- The page will have a rectangular HTML5 canvas, an image uploader, and a button to add text.
- The user can upload images, which will automatically be rendered inside the canvas.
- The user should be able to select an image inside the canvas and drag, resize (by dragging from the image corners), and rotate it.
- The user can click the button to add text inside the canvas.
- The user should be able to edit the text, select, drag, resize, and rotate it.
You can use any JS framework, TypeScript, or native JS, but not canvas JS libraries.
This project uses a canvas-style hit testing system. All elements (images and text objects) are stored in an array and redrawn on each interaction such as element addition, editing, movement, resizing, or rotation. The canvas is cleared and redrawn in a loop to reflect the current state.
The general approach:
- Maintain a list of drawable elements (images/text) with their position, scale, rotation, and other metadata.
- On every mutation (e.g. drag, resize, rotate, edit), the entire canvas is redrawn to ensure visual consistency.
- Hit testing for selection is done manually based on the mouse position and transformation state of each element.
- No external canvas libraries are used — all interactions and rendering are implemented using the native Canvas 2D API.
While functional, performance can be improved, especially when handling multiple elements or complex redraws. Some areas of potential optimization:
- Minimize redraws: Instead of redrawing on every mouse move, throttle or debounce updates when possible.
- Selective computation: Perform transformation math (rotation, hitbox recalculation) only when the relevant state changes.
The first working version was completed in approximately 6 hours. This initial build included:
- Image upload and rendering
- Drag-and-drop repositioning
- Basic text addition
- Element selection via manual hit testing
It was a functional proof-of-concept demonstrating core interactivity, with unpolished UI and minimal error handling.
A cleaner, refactored version of the code was unfortunately lost due to deletion. While this version is not recoverable, the underlying structure and logic are retained and can be clearly explained if needed. The original plan for the refactor focused on separation of concerns, cleaner element abstraction, and improved modularity of the rendering pipeline.