Skip to content

feat: pinch zoom (#84) - #198

Merged
tbolis merged 4 commits into
release/1.1.0from
feat/pinch-zoom
Oct 30, 2026
Merged

tbolis merged 4 commits into
release/1.1.0from
feat/pinch-zoom

Conversation

@tbolis

@tbolis tbolis commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds pinch zoom for 1.1.0 (issue #84).

The branch is rebased on release/1.1.0, which has #197 (polygon), the arrow fixes (#200, #201) and the line style (#202). The commits here are feat: pinch zoom, its docs, and the showcase.

  • Two fingers on the canvas zoom and pan its view. The point between the fingers stays under them while the distance between them sets the zoom, and moving them together pans.
  • New: the pinchZoom property (default true), minZoom (0.5) and maxZoom (8), the onViewChange({ zoom, x, y }) callback, and the resetView() method. The canvas cannot be taken out of sight: a quarter of its smaller side stays in the window.
  • The view is the viewport transform of the canvas, the objects stay where they are. So it is not in toJSON(), it is not an undo step, and onChange is not called for it. It is not zoom(factor), which scales the objects.
  • The second finger makes the first one a gesture of the view, so what the first finger started is dropped: the objects it added are taken off the canvas and out of the undo history, a free stroke stops, the eraser erases nothing, the laser line is released, and a polygon in progress keeps its vertices and places none. This needs a cancel() hook on the tools, a cancel() on the eraser and laser brushes, and discard() on the history.
  • fabric 7.4 cannot do this: its gestures need a library it no longer has, and it ignores the second finger, so the touch events of the canvas are read in the new pinch.ts.
  • README: properties, callback, method and a Pinch zoom section. CHANGELOG: 1.1.0 entry.
  • Showcase: a View section (the pinchZoom switch, the smallest and the largest zoom, and the view as onViewChange reports it, also in the chip of the section) and a reset view button next to the zoom buttons of the canvas.

Changes of behavior

  • toDataURL() draws the whole drawing, whatever the zoom and pan of the view. Before, the pan tool shifted what the image showed. It is in the CHANGELOG under Changed.
  • The eraser brush follows the first finger only, as the other brushes do. Before, a second finger started a second listener.
  • With the polygon tool, a second finger is a gesture of the view and places no vertex. The test of the polygon for it is updated.

Checks

yarn format:check, yarn lint, yarn typecheck and yarn build pass. yarn test: 361 passing and 1 todo, on React 19 and on React 18 (27 tests for the gesture: the view, the limits, nothing saved or undone, each tool, the polygon in progress, the export, unmount). Changing the code on purpose made the tests fail in each case I tried (dropping the strokes, aborting the free stroke, the order of the handlers, onChange, the export, the polygon press).

In headless Chrome, with a real two-finger gesture sent through the DevTools protocol, in the middle of a polygon: the view went to the expected zoom and pan, the vertices placed were unchanged, nothing was added to the sketch, the next tap was placed in the zoomed view, and toDataURL() left the view as it was.

The showcase, built and served, in headless Chrome with a two-finger gesture sent through the DevTools protocol: the view went from 1.00 to 4.00 and the readout and the chip showed it, the reset view button took it back to 1.00, and with the switch off the same gesture changed nothing.

Not checked: a physical touch device.

Notes for review

Two fingers on the canvas zoom and pan its view (#84): the pinchZoom, minZoom and maxZoom properties, the onViewChange callback and resetView(). The view is the viewport of the canvas, the objects stay where they are, so it is not saved, not an undo step, and onChange is not called for it. toDataURL() draws the whole drawing, whatever the view.

When the second finger comes, what the first one started is dropped: the objects it added are taken off the canvas and out of the history, the free stroke stops, and the tool forgets the press (new cancel() hook of the tools, cancel() of the eraser and laser brushes). The eraser brush follows the first finger only, as the other brushes.
The View section has the pinchZoom switch, the smallest and the largest zoom,
and the view as onViewChange reports it (also the chip of the section). The
reset view button is next to the zoom buttons of the canvas.
…uttons

The buttons scale the drawing with zoom(), which is not the view of the
fingers that resetView() puts back, so on a desktop the reset did nothing.
The showcase keeps the factor the buttons scaled by, and the reset scales
by its inverse before it resets the view. The factor starts again when the
canvas is cleared, a value is loaded, or the scenario changes.
@tbolis
tbolis merged commit 026e16f into release/1.1.0 Oct 30, 2026
3 checks passed
@tbolis
tbolis deleted the feat/pinch-zoom branch October 30, 2026 16:19
@tbolis tbolis mentioned this pull request Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant