UNPKG

tldraw

Version:

A tiny little drawing editor.

171 lines (141 loc) 4.05 kB
import { SelectionHandle, ShapeWithCrop, StateNode, TLPointerEventInfo, Vec, kickoutOccludedShapes, } from '@tldraw/editor' import { getCropBox, getDefaultCrop, getUncroppedSize } from '../../../../../shapes/shared/crop' import { CursorTypeMap } from '../../PointingResizeHandle' type Snapshot = ReturnType<Cropping['createSnapshot']> export class Cropping extends StateNode { static override id = 'cropping' info = {} as TLPointerEventInfo & { target: 'selection' handle: SelectionHandle onInteractionEnd?: string | (() => void) } markId = '' private snapshot = {} as any as Snapshot override onEnter( info: TLPointerEventInfo & { target: 'selection' handle: SelectionHandle onInteractionEnd?: string | (() => void) } ) { this.info = info if (typeof info.onInteractionEnd === 'string') { this.parent.setCurrentToolIdMask(info.onInteractionEnd) } this.markId = this.editor.markHistoryStoppingPoint('cropping') this.snapshot = this.createSnapshot() this.updateShapes() } override onPointerMove() { this.updateShapes() } override onKeyDown() { this.updateShapes() } override onKeyUp() { this.updateShapes() } override onPointerUp() { this.complete() } override onComplete() { this.complete() } override onCancel() { this.cancel() } override onExit() { this.parent.setCurrentToolIdMask(undefined) } private updateCursor() { const selectedShape = this.editor.getSelectedShapes()[0] if (!selectedShape) return const cursorType = CursorTypeMap[this.info.handle!] this.editor.setCursor({ type: cursorType, rotation: this.editor.getSelectionRotation() }) } private updateShapes() { const { shape, cursorHandleOffset } = this.snapshot if (!shape) return const util = this.editor.getShapeUtil<ShapeWithCrop>(shape.type) if (!util) return const shiftKey = this.editor.inputs.getShiftKey() const currentPagePoint = this.editor.inputs .getCurrentPagePoint() .clone() .sub(cursorHandleOffset) const originPagePoint = this.editor.inputs.getOriginPagePoint().clone().sub(cursorHandleOffset) const change = currentPagePoint.clone().sub(originPagePoint).rot(-shape.rotation) const crop = shape.props.crop ?? getDefaultCrop() const uncroppedSize = getUncroppedSize(shape.props, crop) const cropFn = util.onCrop?.bind(util) ?? getCropBox const partial = cropFn(shape, { handle: this.info.handle, change, crop, uncroppedSize, initialShape: this.snapshot.shape, aspectRatioLocked: shiftKey, }) if (!partial) return this.editor.updateShapes([ { id: shape.id, type: shape.type, ...partial, }, ]) this.updateCursor() } private complete() { this.updateShapes() kickoutOccludedShapes(this.editor, [this.snapshot.shape.id]) const { onInteractionEnd } = this.info if (onInteractionEnd) { if (typeof onInteractionEnd === 'string') { this.editor.setCurrentTool(onInteractionEnd, this.info) } else { onInteractionEnd() } } else { this.editor.setCroppingShape(null) this.editor.setCurrentTool('select.idle') } } private cancel() { this.editor.bailToMark(this.markId) const { onInteractionEnd } = this.info if (onInteractionEnd) { if (typeof onInteractionEnd === 'string') { this.editor.setCurrentTool(onInteractionEnd, this.info) } else { onInteractionEnd() } } else { this.editor.setCroppingShape(null) this.editor.setCurrentTool('select.idle') } } private createSnapshot() { const selectionRotation = this.editor.getSelectionRotation() const originPagePoint = this.editor.inputs.getOriginPagePoint() const shape = this.editor.getOnlySelectedShape() as ShapeWithCrop const selectionBounds = this.editor.getSelectionRotatedPageBounds()! const dragHandlePoint = Vec.RotWith( selectionBounds.getHandlePoint(this.info.handle!), selectionBounds.point, selectionRotation ) const cursorHandleOffset = Vec.Sub(originPagePoint, dragHandlePoint) return { shape, cursorHandleOffset, } } }