fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
183 lines (175 loc) • 7.53 kB
JavaScript
import { objectSpread2 as _objectSpread2, objectWithoutProperties as _objectWithoutProperties } from '../../_virtual/_rollupPluginBabelHelpers.mjs';
import { resolveOrigin } from '../util/misc/resolveOrigin.mjs';
import { Point } from '../Point.mjs';
import { radiansToDegrees, degreesToRadians } from '../util/misc/radiansDegreesConversion.mjs';
import { isLocked, NOT_ALLOWED_CURSOR, findCornerQuadrant, getLocalPoint } from './util.mjs';
import { wrapWithFireEvent } from './wrapWithFireEvent.mjs';
import { wrapWithFixedAnchor } from './wrapWithFixedAnchor.mjs';
import { CENTER, SKEWING, SCALE_X, SKEW_X, SCALE_Y, SKEW_Y } from '../constants.mjs';
const _excluded = ["target", "ex", "ey", "skewingSide"];
const AXIS_KEYS = {
x: {
counterAxis: 'y',
scale: SCALE_X,
skew: SKEW_X,
lockSkewing: 'lockSkewingX',
origin: 'originX',
flip: 'flipX'
},
y: {
counterAxis: 'x',
scale: SCALE_Y,
skew: SKEW_Y,
lockSkewing: 'lockSkewingY',
origin: 'originY',
flip: 'flipY'
}
};
const skewMap = ['ns', 'nesw', 'ew', 'nwse'];
/**
* return the correct cursor style for the skew 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 skewCursorStyleHandler = (eventData, control, fabricObject) => {
if (control.x !== 0 && isLocked(fabricObject, 'lockSkewingY')) {
return NOT_ALLOWED_CURSOR;
}
if (control.y !== 0 && isLocked(fabricObject, 'lockSkewingX')) {
return NOT_ALLOWED_CURSOR;
}
const n = findCornerQuadrant(fabricObject, control) % 4;
return "".concat(skewMap[n], "-resize");
};
/**
* Since skewing is applied before scaling, calculations are done in a scaleless plane
* @see https://github.com/fabricjs/fabric.js/pull/8380
*/
function skewObject(axis, _ref, pointer) {
let {
target,
ex,
ey,
skewingSide
} = _ref,
transform = _objectWithoutProperties(_ref, _excluded);
const {
skew: skewKey
} = AXIS_KEYS[axis],
offset = pointer.subtract(new Point(ex, ey)).divide(new Point(target.scaleX, target.scaleY))[axis],
skewingBefore = target[skewKey],
skewingStart = transform[skewKey],
shearingStart = Math.tan(degreesToRadians(skewingStart)),
// let a, b be the size of target
// let a' be the value of a after applying skewing
// then:
// a' = a + b * skewA => skewA = (a' - a) / b
// the value b is tricky since skewY is applied before skewX
b = axis === 'y' ? target._getTransformedDimensions({
scaleX: 1,
scaleY: 1,
// since skewY is applied before skewX, b (=width) is not affected by skewX
skewX: 0
}).x : target._getTransformedDimensions({
scaleX: 1,
scaleY: 1
}).y;
const shearing = 2 * offset * skewingSide /
// we max out fractions to safeguard from asymptotic behavior
Math.max(b, 1) +
// add starting state
shearingStart;
const skewing = radiansToDegrees(Math.atan(shearing));
target.set(skewKey, skewing);
const changed = skewingBefore !== target[skewKey];
if (changed && axis === 'y') {
// we don't want skewing to affect scaleX
// so we factor it by the inverse skewing diff to make it seem unchanged to the viewer
const {
skewX,
scaleX
} = target,
dimBefore = target._getTransformedDimensions({
skewY: skewingBefore
}),
dimAfter = target._getTransformedDimensions(),
compensationFactor = skewX !== 0 ? dimBefore.x / dimAfter.x : 1;
compensationFactor !== 1 && target.set(SCALE_X, compensationFactor * scaleX);
}
return changed;
}
/**
* Wrapped Action handler for skewing on a given axis, takes care of the
* skew direction and determines the correct transform origin for the anchor point
* @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
*/
function skewHandler(axis, eventData, transform, x, y) {
const {
target
} = transform,
{
counterAxis,
origin: originKey,
lockSkewing: lockSkewingKey,
skew: skewKey,
flip: flipKey
} = AXIS_KEYS[axis];
if (isLocked(target, lockSkewingKey)) {
return false;
}
const {
origin: counterOriginKey,
flip: counterFlipKey
} = AXIS_KEYS[counterAxis],
counterOriginFactor = resolveOrigin(transform[counterOriginKey]) * (target[counterFlipKey] ? -1 : 1),
// if the counter origin is top/left (= -0.5) then we are skewing x/y values on the bottom/right side of target respectively.
// if the counter origin is bottom/right (= 0.5) then we are skewing x/y values on the top/left side of target respectively.
// skewing direction on the top/left side of target is OPPOSITE to the direction of the movement of the pointer,
// so we factor skewing direction by this value.
skewingSide = -Math.sign(counterOriginFactor) * (target[flipKey] ? -1 : 1),
skewingDirection = (target[skewKey] === 0 &&
// in case skewing equals 0 we use the pointer offset from target center to determine the direction of skewing
getLocalPoint(transform, CENTER, CENTER, x, y)[axis] > 0 ||
// in case target has skewing we use that as the direction
target[skewKey] > 0 ? 1 : -1) * skewingSide,
// anchor to the opposite side of the skewing direction
// normalize value from [-1, 1] to origin value [0, 1]
origin = -skewingDirection * 0.5 + 0.5;
const finalHandler = wrapWithFireEvent(SKEWING, wrapWithFixedAnchor((eventData, transform, x, y) => skewObject(axis, transform, new Point(x, y))));
return finalHandler(eventData, _objectSpread2(_objectSpread2({}, transform), {}, {
[originKey]: origin,
skewingSide
}), x, y);
}
/**
* Wrapped Action handler for skewing on the X axis, takes care of the
* skew direction and determines the correct transform origin for the anchor point
* @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 skewHandlerX = (eventData, transform, x, y) => {
return skewHandler('x', eventData, transform, x, y);
};
/**
* Wrapped Action handler for skewing on the Y axis, takes care of the
* skew direction and determines the correct transform origin for the anchor point
* @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 skewHandlerY = (eventData, transform, x, y) => {
return skewHandler('y', eventData, transform, x, y);
};
export { skewCursorStyleHandler, skewHandlerX, skewHandlerY };
//# sourceMappingURL=skew.mjs.map