UNPKG

tldraw

Version:

A tiny little drawing editor.

141 lines (113 loc) 3.53 kB
import { SelectionHandle, ShapeWithCrop, StateNode, TLPointerEventInfo, Vec } from '@tldraw/editor' import { getCropBox, getDefaultCrop } from '../../../../../shapes/shared/crop' import { kickoutOccludedShapes } from '../../../selectHelpers' 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 } markId = '' private snapshot = {} as any as Snapshot override onEnter( info: TLPointerEventInfo & { target: 'selection' handle: SelectionHandle onInteractionEnd?: string } ) { this.info = info this.markId = this.editor.markHistoryStoppingPoint('cropping') this.snapshot = this.createSnapshot() this.updateShapes() } override onPointerMove() { this.updateShapes() } override onPointerUp() { this.complete() } override onComplete() { this.complete() } override onCancel() { this.cancel() } 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 currentPagePoint = this.editor.inputs.currentPagePoint.clone().sub(cursorHandleOffset) const originPagePoint = this.editor.inputs.originPagePoint.clone().sub(cursorHandleOffset) const change = currentPagePoint.clone().sub(originPagePoint).rot(-shape.rotation) const crop = shape.props.crop ?? getDefaultCrop() const uncroppedSize = { w: (1 / (crop.bottomRight.x - crop.topLeft.x)) * shape.props.w, h: (1 / (crop.bottomRight.y - crop.topLeft.y)) * shape.props.h, } const cropFn = util.onCrop?.bind(util) ?? getCropBox const partial = cropFn(shape, { handle: this.info.handle, change, crop, uncroppedSize, initialShape: this.snapshot.shape, }) 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]) if (this.info.onInteractionEnd) { this.editor.setCurrentTool(this.info.onInteractionEnd, this.info) } else { this.editor.setCroppingShape(null) this.editor.setCurrentTool('select.idle') } } private cancel() { this.editor.bailToMark(this.markId) if (this.info.onInteractionEnd) { this.editor.setCurrentTool(this.info.onInteractionEnd, this.info) } else { this.editor.setCroppingShape(null) this.editor.setCurrentTool('select.idle') } } private createSnapshot() { const selectionRotation = this.editor.getSelectionRotation() const { inputs: { originPagePoint }, } = this.editor 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, } } }