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
JavaScript
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