Forum Discussion
How Rise Canvas was created
Drag, resize and layout
Blocks are absolutely positioned on the canvas surface. Dragging works by tracking mouse delta from the drag start position, scaled by the current zoom level. Resizing works similarly, tracking which handle is being dragged (north, south, east, west, and diagonals) and adjusting width, height and position accordingly.
A zoom control lets users scale the whole canvas in and out without affecting the actual pixel dimensions of the output components. This uses a CSS transform: scale() on the canvas surface.
Design mode
Build mode shows all the block controls, headers, resize handles, edit buttons. Design mode strips all of that away to give a clean view of just the components, making it much easier to assess how the layout actually looks. In design mode, clicking anywhere on a block starts a drag rather than requiring the header, since the header is hidden.
Phase 3 — Improvements
Once the core was solid, I added features in layers, each one making the tool meaningfully better without breaking what was already there.
Smart alignment guides
When dragging a block, the tool compares the dragged block’s six reference points (left edge, right edge, horizontal centre, top edge, bottom edge, vertical centre) against the same six points on every other block. If any pair comes within 8 pixels of each other, a teal dashed guide line appears across the canvas at that position and the block snaps to exact alignment. The guides disappear the moment you release.
Undo / Redo
Every action that changes the canvas state, adding a block, moving it, resizing it, deleting it, duplicating it, stacking, clearing, pushes a clone of the entire blocks array onto a history stack, capped at 50 entries. Ctrl+Z walks backwards through the stack, Ctrl+Y (or Ctrl+Shift+Z) walks forward. The undo and redo toolbar buttons grey out automatically when there’s nothing to undo or redo.
Auto-save
Every time the history stack is pushed, a 2-second debounced timer starts. When it fires, the current blocks array is serialised to JSON and written to localStorage under the key riseCanvas_v1. On next load, the canvas silently reads that key and restores the previous session. If someone’s uploading lots of images (which are stored as base64 strings inside the block HTML), the JSON can get large, browsers allow around 5–10MB for local storage, and if that limit is hit, I ensured the save fails silently.
Image uploads
Several components, Stats & KPI, Expanding Infographic, Flip Card, Carousel, support per-card background or inline images. The upload mechanism uses a hidden <input type="file"> element triggered programmatically. When a file is selected, the browser’s filereader API converts it to a base64 data URL which gets stored directly in the component state. This means the image is self-contained inside the exported HTML, no external hosting required. The trade-off is file size, which is why every upload field I nudged users toward squoosh.app for compression first. I have no link to squoosh I just think it’s brilliantly simple and effective.
Related Content
- 10 months ago
- 9 months ago