UNPKG

tldraw

Version:

A tiny little drawing editor.

8 lines (7 loc) 9.51 kB
{ "version": 3, "sources": ["../../../../../src/lib/tools/ZoomTool/childStates/ZoomQuick.ts"], "sourcesContent": ["import { Box, StateNode, TLKeyboardEventInfo, TLPointerEventInfo, Vec, react } from '@tldraw/editor'\n\nexport class ZoomQuick extends StateNode {\n\tstatic override id = 'zoom_quick'\n\n\tinfo = {} as TLPointerEventInfo & { onInteractionEnd?: string }\n\n\tqzState = 'idle' as 'idle' | 'moving'\n\n\tinitialVpb = new Box()\n\tinitialPp = new Vec()\n\n\t/** The camera zoom right after the overview zoom-out in onEnter. */\n\toverviewZoom = 1\n\n\tcleanupZoomReactor() {\n\t\tvoid null\n\t}\n\n\tnextVpb = new Box()\n\n\toverride onEnter(info: TLPointerEventInfo & { onInteractionEnd: string }) {\n\t\tconst { editor } = this\n\t\tthis.info = info\n\t\tthis.qzState = 'idle'\n\n\t\tthis.initialVpb = editor.getViewportPageBounds()\n\t\tthis.initialPp = Vec.From(editor.inputs.getCurrentPagePoint())\n\n\t\teditor.setCursor({ type: 'zoom-in', rotation: 0 })\n\n\t\t// Find the union of the current viewport and all shapes on the page,\n\t\t// then compute the zoom needed to fit it while preserving cursor position.\n\t\tconst vpb = this.initialVpb\n\t\tconst pageBounds = editor.getCurrentPageBounds()\n\t\tconst commonBounds = pageBounds ? Box.Expand(vpb, pageBounds) : vpb.clone()\n\n\t\t// The cursor stays fixed on screen, so the viewport extends:\n\t\t// left of cursor by sx/z, right by (vsb.w-sx)/z (in page units)\n\t\t// We need each side to reach the common bounds edge.\n\t\tconst vsb = editor.getViewportScreenBounds()\n\t\tconst sp = editor.inputs.getCurrentScreenPoint()\n\t\tconst sx = sp.x - vsb.x\n\t\tconst sy = sp.y - vsb.y\n\t\tconst { x: px, y: py } = this.initialPp\n\n\t\tconst dLeft = px - commonBounds.minX\n\t\tconst dRight = commonBounds.maxX - px\n\t\tconst dTop = py - commonBounds.minY\n\t\tconst dBottom = commonBounds.maxY - py\n\n\t\tlet targetZoom = editor.getCamera().z\n\t\tif (dLeft > 0) targetZoom = Math.min(targetZoom, sx / dLeft)\n\t\tif (dRight > 0) targetZoom = Math.min(targetZoom, (vsb.w - sx) / dRight)\n\t\tif (dTop > 0) targetZoom = Math.min(targetZoom, sy / dTop)\n\t\tif (dBottom > 0) targetZoom = Math.min(targetZoom, (vsb.h - sy) / dBottom)\n\n\t\t// Zoom out a little further to add breathing room for dragging\n\t\ttargetZoom *= 0.85\n\n\t\t// Make sure we're not less than the minimum zoom\n\t\ttargetZoom = Math.max(editor.getCameraOptions().zoomSteps[0], targetZoom)\n\t\tthis.overviewZoom = targetZoom\n\n\t\t// When preserving screen bounds, react to zoom changes to resize the brush.\n\t\t// Otherwise the brush keeps fixed page dimensions.\n\t\tif (editor.options.quickZoomPreservesScreenBounds) {\n\t\t\tthis.cleanupZoomReactor = react('zoom change in quick zoom', () => {\n\t\t\t\teditor.getZoomLevel()\n\t\t\t\tthis.updateBrush()\n\t\t\t})\n\t\t}\n\n\t\t// Set the camera \u2014 when the reactor is active it will update the brush automatically.\n\t\tconst { x: cx, y: cy, z: cz } = editor.getCamera()\n\t\tconst ratio = cz / targetZoom\n\t\teditor.setCamera(new Vec((cx + px) * ratio - px, (cy + py) * ratio - py, targetZoom))\n\n\t\tif (!editor.options.quickZoomPreservesScreenBounds) {\n\t\t\tthis.updateBrush()\n\t\t}\n\t}\n\n\toverride onExit() {\n\t\tthis.cleanupZoomReactor()\n\t\tthis.zoomToNewViewport()\n\t\tthis.editor.updateInstanceState({ zoomBrush: null })\n\t}\n\n\toverride onPointerUp() {\n\t\t// Exit the zoom tool entirely, returning to the original tool\n\t\tconst toolId = this.info.onInteractionEnd?.split('.')[0] ?? 'select'\n\t\tthis.editor.setCurrentTool(toolId)\n\t}\n\n\toverride onCancel() {\n\t\tthis.qzState = 'idle'\n\t\t// Exit the zoom tool entirely, returning to the original tool\n\t\tconst toolId = this.info.onInteractionEnd?.split('.')[0] ?? 'select'\n\t\tthis.editor.setCurrentTool(toolId)\n\t}\n\n\toverride onKeyUp(info: TLKeyboardEventInfo) {\n\t\tif (info.key === 'Shift') {\n\t\t\tthis.parent.transition('idle', this.info)\n\t\t}\n\t}\n\n\tprivate updateBrush() {\n\t\tconst { editor } = this\n\t\tconst nextVpb = this.getNextVpb()\n\t\tthis.nextVpb.setTo(nextVpb)\n\t\teditor.updateInstanceState({ zoomBrush: nextVpb.toJson() })\n\t}\n\n\tprivate zoomToNewViewport() {\n\t\tconst { editor } = this\n\t\tswitch (this.qzState) {\n\t\t\tcase 'idle':\n\t\t\t\t// return to original viewport\n\t\t\t\teditor.zoomToBounds(this.initialVpb, { inset: 0 })\n\t\t\t\tbreak\n\t\t\tcase 'moving':\n\t\t\t\t// zoom to the new viewport\n\t\t\t\teditor.zoomToBounds(this.nextVpb, { inset: 0 })\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\toverride onPointerMove() {\n\t\tif (this.qzState !== 'moving') return\n\t\tthis.updateBrush()\n\t}\n\n\toverride onTick() {\n\t\tconst { editor } = this\n\n\t\t// If the user is idle but has moved their camera, transition to the moving state\n\t\tswitch (this.qzState) {\n\t\t\tcase 'idle': {\n\t\t\t\tconst zoomLevel = editor.getZoomLevel()\n\t\t\t\tif (\n\t\t\t\t\tVec.Dist2(editor.inputs.getCurrentPagePoint(), this.initialPp) * zoomLevel >\n\t\t\t\t\teditor.options.dragDistanceSquared / zoomLevel\n\t\t\t\t) {\n\t\t\t\t\tthis.qzState = 'moving'\n\t\t\t\t\tthis.updateBrush()\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'moving':\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tprivate getNextVpb() {\n\t\tconst { editor } = this\n\t\tlet w: number\n\t\tlet h: number\n\t\tif (editor.options.quickZoomPreservesScreenBounds) {\n\t\t\t// Scale the brush page dimensions so that its screen size stays constant\n\t\t\t// as the overview zoom changes. When the user zooms in on the overview,\n\t\t\t// the brush shrinks in page coords (higher target zoom); zooming out expands it.\n\t\t\tconst zoomRatio = this.overviewZoom / editor.getCamera().z\n\t\t\tw = this.initialVpb.w * zoomRatio\n\t\t\th = this.initialVpb.h * zoomRatio\n\t\t} else {\n\t\t\tw = this.initialVpb.w\n\t\t\th = this.initialVpb.h\n\t\t}\n\t\tconst { x, y } = editor.inputs.getCurrentPagePoint()\n\n\t\t// Normalize the offset on the current screen point within the current viewport screen bounds\n\t\tconst vsb = editor.getViewportScreenBounds()\n\t\tconst vsp = editor.inputs.getCurrentScreenPoint()\n\t\tconst { x: nx, y: ny } = new Vec((vsp.x - vsb.x) / vsb.w, (vsp.y - vsb.y) / vsb.h)\n\n\t\treturn new Box(x - nx * w, y - ny * h, w, h)\n\t}\n}\n"], "mappings": "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", "names": [] }