tldraw
Version:
A tiny little drawing editor.
8 lines (7 loc) • 11.5 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../../../../../src/lib/tools/SelectTool/childStates/Crop/children/Cropping.ts"],
"sourcesContent": ["import {\n\tSelectionHandle,\n\tStateNode,\n\tTLBaseShape,\n\tTLImageShape,\n\tTLImageShapeCrop,\n\tTLPointerEventInfo,\n\tTLShapePartial,\n\tVec,\n\tstructuredClone,\n} from '@tldraw/editor'\nimport { kickoutOccludedShapes } from '../../../selectHelpers'\nimport { CursorTypeMap } from '../../PointingResizeHandle'\nimport { MIN_CROP_SIZE } from '../crop-constants'\n\ntype Snapshot = ReturnType<Cropping['createSnapshot']>\n\nexport class Cropping extends StateNode {\n\tstatic override id = 'cropping'\n\n\tinfo = {} as TLPointerEventInfo & {\n\t\ttarget: 'selection'\n\t\thandle: SelectionHandle\n\t\tonInteractionEnd?: string\n\t}\n\n\tmarkId = ''\n\n\tprivate snapshot = {} as any as Snapshot\n\n\toverride onEnter(\n\t\tinfo: TLPointerEventInfo & {\n\t\t\ttarget: 'selection'\n\t\t\thandle: SelectionHandle\n\t\t\tonInteractionEnd?: string\n\t\t}\n\t) {\n\t\tthis.info = info\n\t\tthis.markId = this.editor.markHistoryStoppingPoint('cropping')\n\t\tthis.snapshot = this.createSnapshot()\n\t\tthis.updateShapes()\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateShapes()\n\t}\n\n\toverride onPointerUp() {\n\t\tthis.complete()\n\t}\n\n\toverride onComplete() {\n\t\tthis.complete()\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\tprivate updateCursor() {\n\t\tconst selectedShape = this.editor.getSelectedShapes()[0]\n\t\tif (!selectedShape) return\n\n\t\tconst cursorType = CursorTypeMap[this.info.handle!]\n\t\tthis.editor.setCursor({ type: cursorType, rotation: this.editor.getSelectionRotation() })\n\t}\n\n\tgetDefaultCrop() {\n\t\treturn {\n\t\t\ttopLeft: { x: 0, y: 0 },\n\t\t\tbottomRight: { x: 1, y: 1 },\n\t\t}\n\t}\n\n\tprivate updateShapes() {\n\t\tconst { shape, cursorHandleOffset } = this.snapshot\n\n\t\tif (!shape) return\n\t\tconst util = this.editor.getShapeUtil<TLImageShape>('image')\n\t\tif (!util) return\n\n\t\tconst props = shape.props\n\n\t\tconst currentPagePoint = this.editor.inputs.currentPagePoint.clone().sub(cursorHandleOffset)\n\t\tconst originPagePoint = this.editor.inputs.originPagePoint.clone().sub(cursorHandleOffset)\n\n\t\tconst change = currentPagePoint.clone().sub(originPagePoint).rot(-shape.rotation)\n\n\t\tconst crop = props.crop ?? this.getDefaultCrop()\n\t\tconst newCrop = structuredClone(crop)\n\n\t\tconst newPoint = new Vec(shape.x, shape.y)\n\t\tconst pointDelta = new Vec(0, 0)\n\n\t\t// original (uncropped) width and height of shape\n\t\tconst w = (1 / (crop.bottomRight.x - crop.topLeft.x)) * props.w\n\t\tconst h = (1 / (crop.bottomRight.y - crop.topLeft.y)) * props.h\n\n\t\tlet hasCropChanged = false\n\n\t\t// Set y dimension\n\t\tswitch (this.info.handle) {\n\t\t\tcase 'top':\n\t\t\tcase 'top_left':\n\t\t\tcase 'top_right': {\n\t\t\t\tif (h < MIN_CROP_SIZE) break\n\t\t\t\thasCropChanged = true\n\t\t\t\t// top\n\t\t\t\tnewCrop.topLeft.y = newCrop.topLeft.y + change.y / h\n\t\t\t\tconst heightAfterCrop = h * (newCrop.bottomRight.y - newCrop.topLeft.y)\n\n\t\t\t\tif (heightAfterCrop < MIN_CROP_SIZE) {\n\t\t\t\t\tnewCrop.topLeft.y = newCrop.bottomRight.y - MIN_CROP_SIZE / h\n\t\t\t\t\tpointDelta.y = (newCrop.topLeft.y - crop.topLeft.y) * h\n\t\t\t\t} else {\n\t\t\t\t\tif (newCrop.topLeft.y <= 0) {\n\t\t\t\t\t\tnewCrop.topLeft.y = 0\n\t\t\t\t\t\tpointDelta.y = (newCrop.topLeft.y - crop.topLeft.y) * h\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpointDelta.y = change.y\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'bottom':\n\t\t\tcase 'bottom_left':\n\t\t\tcase 'bottom_right': {\n\t\t\t\tif (h < MIN_CROP_SIZE) break\n\t\t\t\thasCropChanged = true\n\t\t\t\t// bottom\n\t\t\t\tnewCrop.bottomRight.y = Math.min(1, newCrop.bottomRight.y + change.y / h)\n\t\t\t\tconst heightAfterCrop = h * (newCrop.bottomRight.y - newCrop.topLeft.y)\n\n\t\t\t\tif (heightAfterCrop < MIN_CROP_SIZE) {\n\t\t\t\t\tnewCrop.bottomRight.y = newCrop.topLeft.y + MIN_CROP_SIZE / h\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// Set x dimension\n\t\tswitch (this.info.handle) {\n\t\t\tcase 'left':\n\t\t\tcase 'top_left':\n\t\t\tcase 'bottom_left': {\n\t\t\t\tif (w < MIN_CROP_SIZE) break\n\t\t\t\thasCropChanged = true\n\t\t\t\t// left\n\t\t\t\tnewCrop.topLeft.x = newCrop.topLeft.x + change.x / w\n\t\t\t\tconst widthAfterCrop = w * (newCrop.bottomRight.x - newCrop.topLeft.x)\n\n\t\t\t\tif (widthAfterCrop < MIN_CROP_SIZE) {\n\t\t\t\t\tnewCrop.topLeft.x = newCrop.bottomRight.x - MIN_CROP_SIZE / w\n\t\t\t\t\tpointDelta.x = (newCrop.topLeft.x - crop.topLeft.x) * w\n\t\t\t\t} else {\n\t\t\t\t\tif (newCrop.topLeft.x <= 0) {\n\t\t\t\t\t\tnewCrop.topLeft.x = 0\n\t\t\t\t\t\tpointDelta.x = (newCrop.topLeft.x - crop.topLeft.x) * w\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpointDelta.x = change.x\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'right':\n\t\t\tcase 'top_right':\n\t\t\tcase 'bottom_right': {\n\t\t\t\tif (w < MIN_CROP_SIZE) break\n\t\t\t\thasCropChanged = true\n\t\t\t\t// right\n\t\t\t\tnewCrop.bottomRight.x = Math.min(1, newCrop.bottomRight.x + change.x / w)\n\t\t\t\tconst widthAfterCrop = w * (newCrop.bottomRight.x - newCrop.topLeft.x)\n\n\t\t\t\tif (widthAfterCrop < MIN_CROP_SIZE) {\n\t\t\t\t\tnewCrop.bottomRight.x = newCrop.topLeft.x + MIN_CROP_SIZE / w\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif (!hasCropChanged) return\n\n\t\tnewPoint.add(pointDelta.rot(shape.rotation))\n\n\t\tconst partial: TLShapePartial<\n\t\t\tTLBaseShape<string, { w: number; h: number; crop: TLImageShapeCrop }>\n\t\t> = {\n\t\t\tid: shape.id,\n\t\t\ttype: shape.type,\n\t\t\tx: newPoint.x,\n\t\t\ty: newPoint.y,\n\t\t\tprops: {\n\t\t\t\tcrop: newCrop,\n\t\t\t\tw: (newCrop.bottomRight.x - newCrop.topLeft.x) * w,\n\t\t\t\th: (newCrop.bottomRight.y - newCrop.topLeft.y) * h,\n\t\t\t},\n\t\t}\n\n\t\tthis.editor.updateShapes([partial])\n\t\tthis.updateCursor()\n\t}\n\n\tprivate complete() {\n\t\tthis.updateShapes()\n\t\tkickoutOccludedShapes(this.editor, [this.snapshot.shape.id])\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, this.info)\n\t\t} else {\n\t\t\tthis.editor.setCroppingShape(null)\n\t\t\tthis.editor.setCurrentTool('select.idle')\n\t\t}\n\t}\n\n\tprivate cancel() {\n\t\tthis.editor.bailToMark(this.markId)\n\t\tif (this.info.onInteractionEnd) {\n\t\t\tthis.editor.setCurrentTool(this.info.onInteractionEnd, this.info)\n\t\t} else {\n\t\t\tthis.editor.setCroppingShape(null)\n\t\t\tthis.editor.setCurrentTool('select.idle')\n\t\t}\n\t}\n\n\tprivate createSnapshot() {\n\t\tconst selectionRotation = this.editor.getSelectionRotation()\n\t\tconst {\n\t\t\tinputs: { originPagePoint },\n\t\t} = this.editor\n\n\t\tconst shape = this.editor.getOnlySelectedShape() as TLImageShape\n\n\t\tconst selectionBounds = this.editor.getSelectionRotatedPageBounds()!\n\n\t\tconst dragHandlePoint = Vec.RotWith(\n\t\t\tselectionBounds.getHandlePoint(this.info.handle!),\n\t\t\tselectionBounds.point,\n\t\t\tselectionRotation\n\t\t)\n\n\t\tconst cursorHandleOffset = Vec.Sub(originPagePoint, dragHandlePoint)\n\n\t\treturn {\n\t\t\tshape,\n\t\t\tcursorHandleOffset,\n\t\t}\n\t}\n}\n"],
"mappings": "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",
"names": []
}