UNPKG

fabric

Version:

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

229 lines (221 loc) 7.5 kB
import { controlsUtils, util, Point } from 'fabric'; const { wrapWithFixedAnchor, wrapWithFireEvent } = controlsUtils; /** * Wrap controlsUtils.changeObjectWidth with image constrains */ const changeImageWidth = (eventData, transform, x, y) => { const { target } = transform; const { width } = target; const image = target; 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; }; const changeCropWidth = wrapWithFireEvent('CROPPING', wrapWithFixedAnchor(changeImageWidth)); /** * Wrap controlsUtils.changeObjectHeight with image constrains */ const changeImageHeight = (eventData, transform, x, y) => { const { target } = transform; const { height } = target; const image = target; 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; }; const changeCropHeight = wrapWithFireEvent('CROPPING', wrapWithFixedAnchor(changeImageHeight)); const changeImageCropX = (eventData, transform, x, y) => { const { target } = transform; const image = target; 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; }; const changeImageCropY = (eventData, transform, x, y) => { const { target } = transform; const image = target; 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; }; const changeCropX = wrapWithFireEvent('CROPPING', wrapWithFixedAnchor(changeImageCropX)); const changeCropY = wrapWithFireEvent('CROPPING', 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 */ const cropPanMoveHandler = _ref => { let { transform } = _ref; // this makes the image pan too fast. const { target, original } = transform; const fabricImage = target; 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; }; /** * This position handler works only for this specific use case. * It does not support padding nor offset, and it reduces all possible positions * to the main 4 corners only. * Any position that is < 0 is the extreme left/top, the rest are right/bottom */ function ghostScalePositionHandler(dim, // currentDimension finalMatrix, fabricObject // currentControl: Control, ) { const matrix = fabricObject.calcTransformMatrix(); const vpt = fabricObject.getViewportTransform(); const _finalMatrix = util.multiplyTransformMatrices(vpt, matrix); let x = 0; let y = 0; if (this.x < 0) { x = -fabricObject.width / 2 - fabricObject.cropX; } else { x = fabricObject.getElement().width - fabricObject.width / 2 - fabricObject.cropX; } if (this.y < 0) { y = -fabricObject.height / 2 - fabricObject.cropY; } else { y = fabricObject.getElement().height - fabricObject.height / 2 - fabricObject.cropY; } return new Point(x, y).transform(_finalMatrix); } const calcScale = (currentPoint, height, width) => Math.min(Math.abs(currentPoint.x / width), Math.abs(currentPoint.y / height)); /** * Action handler generator that handles scaling of an image in crop mode. * The goal is to keep the current bounding box steady. * So this action handler has its own calculations for a dynamic anchor point */ const scaleEquallyCropGenerator = (cx, cy) => (eventData, transform, x, y) => { const { target } = transform; const { width: fullWidth, height: fullHeight } = target.getElement(); const remainderX = fullWidth - target.width - target.cropX; const remainderY = fullHeight - target.height - target.cropY; const anchorOriginX = cx < 0 ? 1 + remainderX / target.width : -target.cropX / target.width; const anchorOriginY = cy < 0 ? 1 + remainderY / target.height : -target.cropY / target.height; const constraint = target.translateToOriginPoint(target.getCenterPoint(), anchorOriginX, anchorOriginY); const newPoint = controlsUtils.getLocalPoint(transform, anchorOriginX, anchorOriginY, x, y); const scale = calcScale(newPoint, fullHeight, fullWidth); const scaleChangeX = scale / target.scaleX; const scaleChangeY = scale / target.scaleY; const scaledRemainderX = remainderX / scaleChangeX; const scaledRemainderY = remainderY / scaleChangeY; const newWidth = target.width / scaleChangeX; const newHeight = target.height / scaleChangeY; const newCropX = cx < 0 ? fullWidth - newWidth - scaledRemainderX : target.cropX / scaleChangeX; const newCropY = cy < 0 ? fullHeight - newHeight - scaledRemainderY : target.cropY / scaleChangeY; if ((cx < 0 ? scaledRemainderX : newCropX) + newWidth > fullWidth || (cy < 0 ? scaledRemainderY : newCropY) + newHeight > fullHeight) { return false; } target.scaleX = scale; target.scaleY = scale; target.width = newWidth; target.height = newHeight; target.cropX = newCropX; target.cropY = newCropY; const newAnchorOriginX = cx < 0 ? 1 + scaledRemainderX / newWidth : -newCropX / newWidth; const newAnchorOriginY = cy < 0 ? 1 + scaledRemainderY / newHeight : -newCropY / newHeight; target.setPositionByOrigin(constraint, newAnchorOriginX, newAnchorOriginY); return true; }; function renderGhostImage(_ref2) { let { ctx } = _ref2; const alpha = ctx.globalAlpha; ctx.globalAlpha *= 0.5; ctx.drawImage(this._element, -this.width / 2 - this.cropX, -this.height / 2 - this.cropY); ctx.globalAlpha = alpha; } export { changeCropHeight, changeCropWidth, changeCropX, changeCropY, changeImageCropX, changeImageCropY, changeImageHeight, changeImageWidth, cropPanMoveHandler, ghostScalePositionHandler, renderGhostImage, scaleEquallyCropGenerator }; //# sourceMappingURL=croppingHandlers.mjs.map