UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

1 lines 21.2 kB
{"version":3,"file":"croppingHandlers.mjs","names":[],"sources":["../../extensions/cropping_controls/croppingHandlers.ts"],"sourcesContent":["import type {\n TModificationEvents,\n TransformActionHandler,\n FabricImage,\n ObjectEvents,\n Control,\n TMat2D,\n} from 'fabric';\nimport { controlsUtils, Point, util } from 'fabric';\n\nconst { wrapWithFixedAnchor, wrapWithFireEvent } = controlsUtils;\n\n/**\n * Wraps a handler to swap behavior based on flip state.\n */\nexport const withFlip = (\n handler: TransformActionHandler,\n flippedHandler: TransformActionHandler,\n axis: 'flipX' | 'flipY',\n): TransformActionHandler => {\n return (eventData, transform, x, y) => {\n if (transform.target[axis]) {\n return flippedHandler(eventData, transform, x, y);\n }\n return handler(eventData, transform, x, y);\n };\n};\n\n/**\n * Wraps corner handlers to swap both X and Y behavior based on flip state.\n */\nexport const withCornerFlip = (\n xHandler: TransformActionHandler,\n xFlippedHandler: TransformActionHandler,\n yHandler: TransformActionHandler,\n yFlippedHandler: TransformActionHandler,\n): TransformActionHandler => {\n return (eventData, transform, x, y) => {\n const target = transform.target as FabricImage;\n const xResult = (target.flipX ? xFlippedHandler : xHandler)(\n eventData,\n transform,\n x,\n y,\n );\n const yResult = (target.flipY ? yFlippedHandler : yHandler)(\n eventData,\n transform,\n x,\n y,\n );\n return xResult || yResult;\n };\n};\n\n/**\n * Wrap controlsUtils.changeObjectWidth with image constrains\n */\nexport const changeImageWidth: TransformActionHandler = (\n eventData,\n transform,\n x,\n y,\n) => {\n const { target } = transform;\n const { width } = target;\n const image = target as FabricImage;\n const modified = controlsUtils.changeObjectWidth(eventData, transform, x, y);\n const availableWidth = image._element.width - image.cropX;\n if (modified) {\n if (image.width > availableWidth) {\n image.width = availableWidth;\n }\n if (image.width < 1) {\n image.width = 1;\n }\n }\n return width !== image.width;\n};\n\nexport const changeCropWidth = wrapWithFireEvent(\n 'CROPPING' as TModificationEvents,\n wrapWithFixedAnchor(changeImageWidth),\n);\n\n/**\n * Wrap controlsUtils.changeObjectHeight with image constrains\n */\nexport const changeImageHeight: TransformActionHandler = (\n eventData,\n transform,\n x,\n y,\n) => {\n const { target } = transform;\n const { height } = target;\n const image = target as FabricImage;\n const modified = controlsUtils.changeObjectHeight(eventData, transform, x, y);\n const availableHeight = image._element.height - image.cropY;\n if (modified) {\n if (image.height > availableHeight) {\n image.height = availableHeight;\n }\n if (image.height < 1) {\n image.height = 1;\n }\n }\n return height !== image.height;\n};\n\nexport const changeCropHeight = wrapWithFireEvent(\n 'CROPPING' as TModificationEvents,\n wrapWithFixedAnchor(changeImageHeight),\n);\n\nexport const changeImageCropX: TransformActionHandler = (\n eventData,\n transform,\n x,\n y,\n) => {\n const { target } = transform;\n const image = target as FabricImage;\n const { width, cropX } = image;\n const modified = controlsUtils.changeObjectWidth(eventData, transform, x, y);\n let newCropX = cropX + width - image.width;\n image.width = width;\n if (modified) {\n if (newCropX < 0) {\n newCropX = 0;\n }\n image.cropX = newCropX;\n // calculate new width on the base of how much crop we have now\n image.width += cropX - newCropX;\n }\n return newCropX !== cropX;\n};\n\nexport const changeImageCropY: TransformActionHandler = (\n eventData,\n transform,\n x,\n y,\n) => {\n const { target } = transform;\n const image = target as FabricImage;\n const { height, cropY } = image;\n const modified = controlsUtils.changeObjectHeight(eventData, transform, x, y);\n let newCropY = cropY + height - image.height;\n image.height = height;\n if (modified) {\n if (newCropY < 0) {\n newCropY = 0;\n }\n image.cropY = newCropY;\n image.height += cropY - newCropY;\n }\n return newCropY !== cropY;\n};\n\nexport const changeCropX = wrapWithFireEvent(\n 'CROPPING' as TModificationEvents,\n wrapWithFixedAnchor(changeImageCropX),\n);\n\nexport const changeCropY = wrapWithFireEvent(\n 'CROPPING' as TModificationEvents,\n wrapWithFixedAnchor(changeImageCropY),\n);\n\n/**\n * A function to counter the move action and change cropX/cropY of an image\n * Keep the image steady, but moves it inside its own cropping rectangle\n */\nexport const cropPanMoveHandler = ({ transform }: ObjectEvents['moving']) => {\n // this makes the image pan too fast.\n const { target, original } = transform;\n const fabricImage = target as FabricImage;\n const p = new Point(\n target.left - original.left,\n target.top - original.top,\n ).transform(\n util.invertTransform(\n util.createRotateMatrix({ angle: fabricImage.getTotalAngle() }),\n ),\n );\n let cropX =\n original.cropX! - (p.x / fabricImage.scaleX) * (fabricImage.flipX ? -1 : 1);\n let cropY =\n original.cropY! - (p.y / fabricImage.scaleY) * (fabricImage.flipY ? -1 : 1);\n const { width, height, _element } = fabricImage;\n if (cropX < 0) {\n cropX = 0;\n }\n if (cropY < 0) {\n cropY = 0;\n }\n if (cropX + width > _element.width) {\n cropX = _element.width - width;\n }\n if (cropY + height > _element.height) {\n cropY = _element.height - height;\n }\n fabricImage.cropX = cropX;\n fabricImage.cropY = cropY;\n fabricImage.left = original.left;\n fabricImage.top = original.top;\n};\n\n/**\n * This position handler works only for this specific use case.\n * It does not support padding nor offset, and it reduces all possible positions\n * to the main 4 corners only.\n * Any position that is < 0 is the extreme left/top, the rest are right/bottom\n */\nexport function ghostScalePositionHandler(\n this: Control,\n dim: Point, // currentDimension\n finalMatrix: TMat2D,\n fabricObject: FabricImage,\n // currentControl: Control,\n) {\n const matrix = fabricObject.calcTransformMatrix();\n const vpt = fabricObject.getViewportTransform();\n const _finalMatrix = util.multiplyTransformMatrices(vpt, matrix);\n\n const x =\n this.x < 0\n ? -fabricObject.width / 2 - fabricObject.cropX\n : fabricObject.getElement().width -\n fabricObject.width / 2 -\n fabricObject.cropX;\n\n const y =\n this.y < 0\n ? -fabricObject.height / 2 - fabricObject.cropY\n : fabricObject.getElement().height -\n fabricObject.height / 2 -\n fabricObject.cropY;\n return new Point(x, y).transform(_finalMatrix);\n}\n\nconst calcScale = (currentPoint: Point, height: number, width: number) =>\n Math.min(Math.abs(currentPoint.x / width), Math.abs(currentPoint.y / height));\n\nconst flipNumericOrigin = (origin: number, flipped: boolean) =>\n flipped ? 1 - origin : origin;\n\n/**\n * Reflects pointer position across object center when image is flipped.\n * This compensates for the inverted local coordinate system.\n */\n// const reflectPointerForFlip = (\n// target: FabricImage,\n// x: number,\n// y: number,\n// ): Point => {\n// if (!target.flipX && !target.flipY) {\n// return new Point(x, y);\n// }\n// const center = target.getCenterPoint();\n// return new Point(\n// target.flipX ? center.x - x : x,\n// target.flipY ? center.y - y : y,\n// );\n// };\n\n/**\n * Action handler generator that handles scaling of an image in crop mode.\n * The goal is to keep the current bounding box steady.\n * So this action handler has its own calculations for a dynamic anchor point\n */\nexport const scaleEquallyCropGenerator =\n (cx: number, cy: number): TransformActionHandler =>\n (eventData, transform, x, y) => {\n const { target } = transform as unknown as { target: FabricImage };\n const { width: fullWidth, height: fullHeight } = target.getElement();\n const remainderX = fullWidth - target.width - target.cropX;\n const remainderY = fullHeight - target.height - target.cropY;\n const anchorOriginX = flipNumericOrigin(\n cx < 0 ? 1 + remainderX / target.width : -target.cropX / target.width,\n target.flipX,\n );\n const anchorOriginY = flipNumericOrigin(\n cy < 0 ? 1 + remainderY / target.height : -target.cropY / target.height,\n target.flipY,\n );\n const constraint = target.translateToOriginPoint(\n target.getCenterPoint(),\n anchorOriginX,\n anchorOriginY,\n );\n\n const newPoint = controlsUtils.getLocalPoint(\n transform,\n anchorOriginX,\n anchorOriginY,\n x,\n y,\n );\n\n const scale = calcScale(newPoint, fullHeight, fullWidth);\n\n const scaleChangeX = scale / target.scaleX;\n const scaleChangeY = scale / target.scaleY;\n const scaledRemainderX = remainderX / scaleChangeX;\n const scaledRemainderY = remainderY / scaleChangeY;\n const newWidth = target.width / scaleChangeX;\n const newHeight = target.height / scaleChangeY;\n const newCropX =\n cx < 0\n ? fullWidth - newWidth - scaledRemainderX\n : target.cropX / scaleChangeX;\n const newCropY =\n cy < 0\n ? fullHeight - newHeight - scaledRemainderY\n : target.cropY / scaleChangeY;\n\n const boundsFailX =\n (cx < 0 ? scaledRemainderX : newCropX) + newWidth > fullWidth;\n const boundsFailY =\n (cy < 0 ? scaledRemainderY : newCropY) + newHeight > fullHeight;\n\n if (boundsFailX || boundsFailY) {\n return false;\n }\n\n target.scaleX = scale;\n target.scaleY = scale;\n target.width = newWidth;\n target.height = newHeight;\n target.cropX = newCropX;\n target.cropY = newCropY;\n const newAnchorOriginX = flipNumericOrigin(\n cx < 0 ? 1 + scaledRemainderX / newWidth : -newCropX / newWidth,\n target.flipX,\n );\n const newAnchorOriginY = flipNumericOrigin(\n cy < 0 ? 1 + scaledRemainderY / newHeight : -newCropY / newHeight,\n target.flipY,\n );\n\n target.setPositionByOrigin(constraint, newAnchorOriginX, newAnchorOriginY);\n return true;\n };\n\nexport function renderGhostImage(\n this: FabricImage,\n { ctx }: { ctx: CanvasRenderingContext2D },\n) {\n const element = this._element;\n const ghostX = -this.width / 2 - this.cropX;\n const ghostY = -this.height / 2 - this.cropY;\n\n const alpha = ctx.globalAlpha;\n ctx.globalAlpha *= 0.5;\n ctx.drawImage(element, ghostX, ghostY);\n\n ctx.strokeStyle = this.borderColor;\n // we assume this.scaleX and this.scaleY are same in an image.\n // it is not common use case to stretch images, and if it is, and is brought up,\n // this border for the image needs to be drawn differently.\n ctx.lineWidth = this.borderScaleFactor / this.scaleX;\n ctx.strokeRect(ghostX, ghostY, element.width, element.height);\n\n ctx.globalAlpha = alpha;\n}\n\nconst { capValue } = util;\n\n/**\n * Those are controls used to resize an image, similar to cropX,cropY,width,height\n * But they change the scale of an image to accomodate out of bounds resizing.\n * When resize comes back they scale the image back to what was before.\n * The memory effect for bounce back works for the same transform.\n * Once you mouseup, the bounce back is lost.\n */\nconst changeImageSizeWithAutoCoverGenerator =\n (axis: 'x' | 'y'): TransformActionHandler =>\n (_eventData, transform, x, y) => {\n const image = transform.target as FabricImage;\n const original = transform.original;\n\n const isX = axis === 'x';\n const isFlipped = isX ? image.flipX : image.flipY;\n const elementSize = isX ? image._element.width : image._element.height;\n const crossElementSize = isX ? image._element.height : image._element.width;\n const isNegativeEdge = isX\n ? transform.originX === 'right'\n : transform.originY === 'bottom';\n\n const initialSize = isX ? transform.width : transform.height;\n const initialCrossSize = isX ? transform.height : transform.width;\n const initialCrop = isX ? (original.cropX ?? 0) : (original.cropY ?? 0);\n const initialCrossCrop = isX\n ? (original.cropY ?? 0)\n : (original.cropX ?? 0);\n const initialScale = isX ? original.scaleX : original.scaleY;\n const initialCrossScale = isX ? original.scaleY : original.scaleX;\n\n const localPoint = controlsUtils.getLocalPoint(\n transform,\n transform.originX,\n transform.originY,\n x,\n y,\n );\n\n const coordinate = isX ? localPoint.x : localPoint.y;\n const rawSize = isNegativeEdge ? -coordinate : coordinate;\n const requestedSize = Math.max(10, rawSize / initialScale);\n\n const availableSize =\n isNegativeEdge !== isFlipped\n ? initialCrop + initialSize\n : elementSize - initialCrop;\n\n const setImageProps = (\n size: number,\n crossSize: number,\n scale: number,\n crop: number,\n crossCrop: number,\n ) => {\n if (isX) {\n image.width = size;\n image.height = crossSize;\n image.cropX = crop;\n image.cropY = crossCrop;\n } else {\n image.height = size;\n image.width = crossSize;\n image.cropY = crop;\n image.cropX = crossCrop;\n }\n image.scaleX = scale;\n image.scaleY = scale;\n };\n\n if (requestedSize <= availableSize) {\n const newCrop =\n isNegativeEdge !== isFlipped\n ? Math.max(0, initialCrop + initialSize - requestedSize)\n : initialCrop;\n setImageProps(\n Math.max(1, requestedSize),\n initialCrossSize,\n initialScale,\n newCrop,\n initialCrossCrop,\n );\n } else {\n const targetScaledSize = requestedSize * initialScale;\n const newScale = targetScaledSize / availableSize;\n\n const scaledCrossSize = initialCrossSize * initialCrossScale;\n const crossNaturalInView = scaledCrossSize / newScale;\n const newCrossSize = Math.min(crossNaturalInView, crossElementSize);\n const crossCenter = initialCrossCrop + initialCrossSize / 2;\n const newCrossCrop = capValue(\n crossCenter - newCrossSize / 2,\n 0,\n crossElementSize - newCrossSize,\n );\n\n setImageProps(\n availableSize,\n newCrossSize,\n newScale,\n isNegativeEdge !== isFlipped ? 0 : initialCrop,\n newCrossCrop,\n );\n }\n\n return true;\n };\n\nexport const changeImageWidthWithAutoCover =\n changeImageSizeWithAutoCoverGenerator('x');\nexport const changeImageHeightWithAutoCover =\n changeImageSizeWithAutoCoverGenerator('y');\n\nexport const changeWidthAndScaleToCover = wrapWithFireEvent(\n 'RESIZING' as TModificationEvents,\n wrapWithFixedAnchor(changeImageWidthWithAutoCover),\n);\n\nexport const changeHeightAndScaleToCover = wrapWithFireEvent(\n 'RESIZING' as TModificationEvents,\n wrapWithFixedAnchor(changeImageHeightWithAutoCover),\n);\n"],"mappings":"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"}