Building a Canvas‑Based Resume Editor: A Frontend Engineer’s Journey
The author details how a month‑long personal project uses HTML Canvas to implement a fully functional resume editor, covering custom data structures, undo/redo history, layered rendering, event handling, focus management, infinite canvas support, performance tricks, and future enhancements.
Background
The author, motivated by curiosity and the desire to avoid paid resume services, spent nearly a month building a resume editor using Canvas instead of a DOM‑based approach. The goal was learning, so most utilities (e.g., ArcoDesign, ResizeObserve, Jest) were implemented manually, including packages like packages/delta, packages/plugin, and packages/core.
Data Structure
The editor stores changes in a flat DeltaSet while the core Core module manages a tree‑like State. To support undo/redo without full snapshots, atomic operations ( Op) are recorded, and the history is built from these ops. The design prefers a flat structure to simplify look‑ups, reducing the number of Op types while still enabling a full History feature.
History
Atomic Op objects are pre‑designed, allowing the History module to avoid full snapshot storage. Operations are batched into a History Stack after a configurable idle period (N ms). If an Undo occurs during the wait, the timer is cleared, the pending ops are moved to the Redo Stack, and the batch is committed.
Drawing
All visual elements are rectangles; rendering uses two overlapping Canvas layers. The inner canvas draws actual shapes with incremental updates, while the outer canvas renders transient states such as selection boxes, multi‑select, and resize handles. Because there is no native DOM, positions are calculated from mouse events ( MouseDown, MouseMove, MouseUp, Hover) and stored in a Payload that drives drawing.
Drawing State
Initially the author used a chaotic event‑driven approach that called Mask methods directly. Later the logic was refactored into a central Store with custom event dispatch, allowing strict control over which UI should be redrawn. The state machine still relies on many inter‑related if/else branches, but it makes the source of each change explicit.
Rendering & Events
Rendering mimics DOM order: parents are drawn before children using a depth‑first traversal, but children are pre‑sorted by zIndex to preserve overlap semantics. Event simulation follows the opposite order—higher‑zIndex elements are examined first, and traversal stops once the target is found. To avoid costly per‑frame tree walks on high‑frequency onMouseMove, the author caches child lists and updates parent references only when a node changes, turning recursion into iteration and using two stacks to emulate capture and bubbling.
Focus
Because Canvas cannot receive focus, two solutions are explored: (1) give the canvas a tabIndex="-1 attribute and read document.activeElement; (2) overlay a transparent div with pointerEvents: none and query window.getSelection to determine focus. Either method lets the editor ignore undo/redo shortcuts when the canvas is not focused.
Infinite Canvas
Adding pan‑and‑zoom later revealed missing design for an infinite canvas. Issues such as mismatched Mask refresh rates, incorrect ctx.translate signs, and out‑of‑bounds clipping were fixed with targeted adjustments rather than a full rewrite.
Performance Optimizations
Skip drawing elements completely outside the visible area.
Only redraw elements affected by the current operation.
Separate high‑frequency drawing onto an upper canvas and static elements onto a lower canvas.
Throttle batch drawing for rapid interactions, collecting dependencies on the upper canvas before a single render pass.
Hyperlinks
Since Canvas output is a bitmap, exported PDFs lack clickable links. The author solves this by generating transparent a tags via the DOM at link positions during PDF generation, or by using libraries such as PDFKit/PDFjs to embed link annotations directly.
TODO
Layer ordering controls (currently designed in core but UI missing).
Page size configuration for non‑A4 resumes.
Import/export of the underlying JSON data structure.
Custom PDF pagination to overcome browser print limits.
Copy‑paste module for editing convenience.
Conclusion
The experiment reinforced the author’s appreciation for Canvas‑based graphics, and future posts will dive deeper into encountered challenges. The author’s primary work remains on rich‑text editors, which share many of the same complexities.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
IoT Full-Stack Technology
Dedicated to sharing IoT cloud services, embedded systems, and mobile client technology, with no spam ads.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
