UNPKG

fabric

Version:

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

159 lines (158 loc) 7.61 kB
import { SCALING } from "../constants.mjs"; import { NOT_ALLOWED_CURSOR, findCornerQuadrant, getLocalPoint, invertOrigin, isLocked, isTransformCentered } from "./util.mjs"; import { wrapWithFireEvent } from "./wrapWithFireEvent.mjs"; import { wrapWithFixedAnchor } from "./wrapWithFixedAnchor.mjs"; //#region src/controls/scale.ts /** * Inspect event and fabricObject properties to understand if the scaling action * @param {Event} eventData from the user action * @param {FabricObject} fabricObject the fabric object about to scale * @return {Boolean} true if scale is proportional */ function scaleIsProportional(eventData, fabricObject) { const canvas = fabricObject.canvas, uniformIsToggled = eventData[canvas.uniScaleKey]; return canvas.uniformScaling && !uniformIsToggled || !canvas.uniformScaling && uniformIsToggled; } /** * Inspect fabricObject to understand if the current scaling action is allowed * @param {FabricObject} fabricObject the fabric object about to scale * @param {String} by 'x' or 'y' or '' * @param {Boolean} scaleProportionally true if we are trying to scale proportionally * @return {Boolean} true if scaling is not allowed at current conditions */ function scalingIsForbidden(fabricObject, by, scaleProportionally) { const lockX = isLocked(fabricObject, "lockScalingX"), lockY = isLocked(fabricObject, "lockScalingY"); if (lockX && lockY) return true; if (!by && (lockX || lockY) && scaleProportionally) return true; if (lockX && by === "x") return true; if (lockY && by === "y") return true; const { width, height, strokeWidth } = fabricObject; if (width === 0 && strokeWidth === 0 && by !== "y") return true; if (height === 0 && strokeWidth === 0 && by !== "x") return true; return false; } const scaleMap = [ "e", "se", "s", "sw", "w", "nw", "n", "ne", "e" ]; /** * return the correct cursor style for the scale action * @param {Event} eventData the javascript event that is causing the scale * @param {Control} control the control that is interested in the action * @param {FabricObject} fabricObject the fabric object that is interested in the action * @return {String} a valid css string for the cursor */ const scaleCursorStyleHandler = (eventData, control, fabricObject, coord) => { const scaleProportionally = scaleIsProportional(eventData, fabricObject); if (scalingIsForbidden(fabricObject, control.x !== 0 && control.y === 0 ? "x" : control.x === 0 && control.y !== 0 ? "y" : "", scaleProportionally)) return NOT_ALLOWED_CURSOR; return `${scaleMap[findCornerQuadrant(fabricObject, control, coord)]}-resize`; }; /** * Basic scaling logic, reused with different constrain for scaling X,Y, freely or equally. * Needs to be wrapped with `wrapWithFixedAnchor` to be effective * @param {Event} eventData javascript event that is doing the transform * @param {Object} transform javascript object containing a series of information around the current transform * @param {number} x current mouse x position, canvas normalized * @param {number} y current mouse y position, canvas normalized * @param {Object} options additional information for scaling * @param {String} options.by 'x', 'y', 'equally' or '' to indicate type of scaling * @return {Boolean} true if some change happened * @private */ function scaleObject(eventData, transform, x, y, options = {}) { const target = transform.target, by = options.by, scaleProportionally = scaleIsProportional(eventData, target), forbidScaling = scalingIsForbidden(target, by, scaleProportionally); let newPoint, scaleX, scaleY, dim, signX, signY; if (forbidScaling) return false; if (transform.gestureScale) { scaleX = transform.scaleX * transform.gestureScale; scaleY = transform.scaleY * transform.gestureScale; } else { newPoint = getLocalPoint(transform, transform.originX, transform.originY, x, y); signX = by !== "y" ? Math.sign(newPoint.x || transform.signX || 1) : 1; signY = by !== "x" ? Math.sign(newPoint.y || transform.signY || 1) : 1; if (!transform.signX) transform.signX = signX; if (!transform.signY) transform.signY = signY; if (isLocked(target, "lockScalingFlip") && (transform.signX !== signX || transform.signY !== signY)) return false; dim = target._getTransformedDimensions(); if (scaleProportionally && !by) { const distance = Math.abs(newPoint.x) + Math.abs(newPoint.y), { original } = transform, scale = distance / (Math.abs(dim.x * original.scaleX / target.scaleX) + Math.abs(dim.y * original.scaleY / target.scaleY)); scaleX = original.scaleX * scale; scaleY = original.scaleY * scale; } else { scaleX = Math.abs(newPoint.x * target.scaleX / dim.x); scaleY = Math.abs(newPoint.y * target.scaleY / dim.y); } if (isTransformCentered(transform)) { scaleX *= 2; scaleY *= 2; } if (transform.signX !== signX && by !== "y") { transform.originX = invertOrigin(transform.originX); scaleX *= -1; transform.signX = signX; } if (transform.signY !== signY && by !== "x") { transform.originY = invertOrigin(transform.originY); scaleY *= -1; transform.signY = signY; } } const oldScaleX = target.scaleX, oldScaleY = target.scaleY; if (!by) { !isLocked(target, "lockScalingX") && target.set("scaleX", scaleX); !isLocked(target, "lockScalingY") && target.set("scaleY", scaleY); } else { by === "x" && target.set("scaleX", scaleX); by === "y" && target.set("scaleY", scaleY); } return oldScaleX !== target.scaleX || oldScaleY !== target.scaleY; } /** * Generic scaling logic, to scale from corners either equally or freely. * Needs to be wrapped with `wrapWithFixedAnchor` to be effective * @param {Event} eventData javascript event that is doing the transform * @param {Object} transform javascript object containing a series of information around the current transform * @param {number} x current mouse x position, canvas normalized * @param {number} y current mouse y position, canvas normalized * @return {Boolean} true if some change happened */ const scaleObjectFromCorner = (eventData, transform, x, y) => { return scaleObject(eventData, transform, x, y); }; /** * Scaling logic for the X axis. * Needs to be wrapped with `wrapWithFixedAnchor` to be effective * @param {Event} eventData javascript event that is doing the transform * @param {Object} transform javascript object containing a series of information around the current transform * @param {number} x current mouse x position, canvas normalized * @param {number} y current mouse y position, canvas normalized * @return {Boolean} true if some change happened */ const scaleObjectX = (eventData, transform, x, y) => { return scaleObject(eventData, transform, x, y, { by: "x" }); }; /** * Scaling logic for the Y axis. * Needs to be wrapped with `wrapWithFixedAnchor` to be effective * @param {Event} eventData javascript event that is doing the transform * @param {Object} transform javascript object containing a series of information around the current transform * @param {number} x current mouse x position, canvas normalized * @param {number} y current mouse y position, canvas normalized * @return {Boolean} true if some change happened */ const scaleObjectY = (eventData, transform, x, y) => { return scaleObject(eventData, transform, x, y, { by: "y" }); }; const scalingEqually = wrapWithFireEvent(SCALING, wrapWithFixedAnchor(scaleObjectFromCorner)); const scalingX = wrapWithFireEvent(SCALING, wrapWithFixedAnchor(scaleObjectX)); const scalingY = wrapWithFireEvent(SCALING, wrapWithFixedAnchor(scaleObjectY)); //#endregion export { scaleCursorStyleHandler, scalingEqually, scalingX, scalingY }; //# sourceMappingURL=scale.mjs.map