UNPKG

fabric

Version:

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

163 lines (152 loc) 4.25 kB
import type { TModificationEvents, Transform, TransformActionHandler, FabricImage, ObjectEvents, } from 'fabric'; import { controlsUtils, Point, util } from 'fabric'; const { wrapWithFixedAnchor, wrapWithFireEvent } = controlsUtils; /** * Wrap controlsUtils.changeObjectWidth with image constrains */ export const changeImageWidth: TransformActionHandler = ( eventData, transform, x, y, ) => { const { target } = transform; const { width } = target; const image = target as FabricImage; const modified = controlsUtils.changeObjectWidth(eventData, transform, x, y); const availableWidth = image._element.width - image.cropX; if (modified) { if (image.width > availableWidth) { image.width = availableWidth; } if (image.width < 1) { image.width = 1; } } return width !== image.width; }; export const changeCropWidth = wrapWithFireEvent( 'CROPPING' as TModificationEvents, wrapWithFixedAnchor(changeImageWidth), ); /** * Wrap controlsUtils.changeObjectHeight with image constrains */ export const changeImageHeight: TransformActionHandler = ( eventData, transform, x, y, ) => { const { target } = transform; const { height } = target; const image = target as FabricImage; const modified = controlsUtils.changeObjectHeight(eventData, transform, x, y); const availableHeight = image._element.height - image.cropY; if (modified) { if (image.height > availableHeight) { image.height = availableHeight; } if (image.height < 1) { image.height = 1; } } return height !== image.height; }; export const changeCropHeight = wrapWithFireEvent( 'CROPPING' as TModificationEvents, wrapWithFixedAnchor(changeImageHeight), ); export const changeImageCropX: TransformActionHandler = ( eventData, transform, x, y, ) => { const { target } = transform; const image = target as FabricImage; const { width, cropX } = image; const modified = controlsUtils.changeObjectWidth(eventData, transform, x, y); let newCropX = cropX + width - image.width; image.width = width; if (modified) { if (newCropX < 0) { newCropX = 0; } image.cropX = newCropX; // calculate new width on the base of how much crop we have now image.width += cropX - newCropX; } return newCropX !== cropX; }; export const changeImageCropY: TransformActionHandler = ( eventData, transform, x, y, ) => { const { target } = transform; const image = target as FabricImage; const { height, cropY } = image; const modified = controlsUtils.changeObjectHeight(eventData, transform, x, y); let newCropY = cropY + height - image.height; image.height = height; if (modified) { if (newCropY < 0) { newCropY = 0; } image.cropY = newCropY; image.height += cropY - newCropY; } return newCropY !== cropY; }; export const changeCropX = wrapWithFireEvent( 'CROPPING' as TModificationEvents, wrapWithFixedAnchor(changeImageCropX), ); export const changeCropY = wrapWithFireEvent( 'CROPPING' as TModificationEvents, wrapWithFixedAnchor(changeImageCropY), ); /** * A function to counter the move action and change cropX/cropY of an image * Keep the image steady, but moves it inside its own cropping rectangle */ export const cropPanMoveHandler = ({ transform }: ObjectEvents['moving']) => { // this makes the image pan too fast. const { target, original } = transform as Transform; const fabricImage = target as FabricImage; const p = new Point( target.left - original.left, target.top - original.top, ).transform( util.invertTransform( util.createRotateMatrix({ angle: fabricImage.getTotalAngle() }), ), ); let cropX = original.cropX! - p.x / fabricImage.scaleX; let cropY = original.cropY! - p.y / fabricImage.scaleY; const { width, height, _element } = fabricImage; if (cropX < 0) { cropX = 0; } if (cropY < 0) { cropY = 0; } if (cropX + width > _element.width) { cropX = _element.width - width; } if (cropY + height > _element.height) { cropY = _element.height - height; } fabricImage.cropX = cropX; fabricImage.cropY = cropY; fabricImage.left = original.left; fabricImage.top = original.top; };