UNPKG

advanced-cropper

Version:

The core of the advanced cropper libraries family

332 lines (329 loc) 14.8 kB
import { __assign } from 'tslib'; import { ALL_DIRECTIONS, HORIZONTAL_DIRECTIONS, VERTICAL_DIRECTIONS } from '../constants/index.js'; import { isGreater, isRoughlyEqual } from '../utils/index.js'; import { moveToPositionRestrictions, applyDirections, getIntersections, getOppositeSide, ratio, getBrokenRatio, applyMove, diff } from '../service/utils.js'; import '../types/index.js'; import '../state/setCoordinates.js'; import { moveCoordinatesAlgorithm } from './moveCoordinatesAlgorithm.js'; function fitDirections(_a) { var coordinates = _a.coordinates, directions = _a.directions, positionRestrictions = _a.positionRestrictions, sizeRestrictions = _a.sizeRestrictions, preserveRatio = _a.preserveRatio, allowedDirections = _a.allowedDirections, _b = _a.compensate, compensate = _b === void 0 ? true : _b; var fittedDirections = __assign({}, directions); var currentWidth = applyDirections(coordinates, fittedDirections).width; var currentHeight = applyDirections(coordinates, fittedDirections).height; // Prevent strange resizes when the width or height of stencil becomes smaller than 0 if (currentWidth < 0) { if (fittedDirections.left < 0 && fittedDirections.right < 0) { fittedDirections.left = -(coordinates.width - sizeRestrictions.minWidth) / (fittedDirections.left / fittedDirections.right); fittedDirections.right = -(coordinates.width - sizeRestrictions.minWidth) / (fittedDirections.right / fittedDirections.left); } else if (fittedDirections.left < 0) { fittedDirections.left = -(coordinates.width - sizeRestrictions.minWidth); } else if (fittedDirections.right < 0) { fittedDirections.right = -(coordinates.width - sizeRestrictions.minWidth); } } if (currentHeight < 0) { if (fittedDirections.top < 0 && fittedDirections.bottom < 0) { fittedDirections.top = -(coordinates.height - sizeRestrictions.minHeight) / (fittedDirections.top / fittedDirections.bottom); fittedDirections.bottom = -(coordinates.height - sizeRestrictions.minHeight) / (fittedDirections.bottom / fittedDirections.top); } else if (fittedDirections.top < 0) { fittedDirections.top = -(coordinates.height - sizeRestrictions.minHeight); } else if (fittedDirections.bottom < 0) { fittedDirections.bottom = -(coordinates.height - sizeRestrictions.minHeight); } } // Prevent breaking limits var breaks = getIntersections(applyDirections(coordinates, fittedDirections), positionRestrictions); var canBeCompensated = ALL_DIRECTIONS.every(function (direction) { return !isGreater(breaks[getOppositeSide(direction)], 0) || allowedDirections[direction]; }); if (compensate && canBeCompensated) { if (breaks.left && breaks.left > 0 && breaks.right === 0) { fittedDirections.right += breaks.left; fittedDirections.left -= breaks.left; } else if (breaks.right && breaks.right > 0 && breaks.left === 0) { fittedDirections.left += breaks.right; fittedDirections.right -= breaks.right; } if (breaks.top && breaks.top > 0 && breaks.bottom === 0) { fittedDirections.bottom += breaks.top; fittedDirections.top -= breaks.top; } else if (breaks.bottom && breaks.bottom > 0 && breaks.top === 0) { fittedDirections.top += breaks.bottom; fittedDirections.bottom -= breaks.bottom; } breaks = getIntersections(applyDirections(coordinates, fittedDirections), positionRestrictions); } var maxResize = { width: Infinity, height: Infinity, left: Infinity, right: Infinity, top: Infinity, bottom: Infinity, }; ALL_DIRECTIONS.forEach(function (direction) { var intersection = breaks[direction]; if (intersection && fittedDirections[direction]) { maxResize[direction] = Math.max(0, 1 - intersection / fittedDirections[direction]); } }); if (preserveRatio) { var multiplier_1 = Math.min.apply(Math, ALL_DIRECTIONS.map(function (direction) { return maxResize[direction]; })); if (multiplier_1 !== Infinity) { ALL_DIRECTIONS.forEach(function (direction) { fittedDirections[direction] *= multiplier_1; }); } } else { ALL_DIRECTIONS.forEach(function (direction) { if (maxResize[direction] !== Infinity) { fittedDirections[direction] *= maxResize[direction]; } }); } currentWidth = applyDirections(coordinates, fittedDirections).width; currentHeight = applyDirections(coordinates, fittedDirections).height; if (fittedDirections.right + fittedDirections.left) { if (currentWidth > sizeRestrictions.maxWidth) { maxResize.width = (sizeRestrictions.maxWidth - coordinates.width) / (fittedDirections.right + fittedDirections.left); } else if (currentWidth < sizeRestrictions.minWidth) { maxResize.width = (sizeRestrictions.minWidth - coordinates.width) / (fittedDirections.right + fittedDirections.left); } } if (fittedDirections.bottom + fittedDirections.top) { if (currentHeight > sizeRestrictions.maxHeight) { maxResize.height = (sizeRestrictions.maxHeight - coordinates.height) / (fittedDirections.bottom + fittedDirections.top); } else if (currentHeight < sizeRestrictions.minHeight) { maxResize.height = (sizeRestrictions.minHeight - coordinates.height) / (fittedDirections.bottom + fittedDirections.top); } } if (preserveRatio) { var multiplier_2 = Math.min(maxResize.width, maxResize.height); if (multiplier_2 !== Infinity) { ALL_DIRECTIONS.forEach(function (direction) { fittedDirections[direction] *= multiplier_2; }); } } else { if (maxResize.width !== Infinity) { HORIZONTAL_DIRECTIONS.forEach(function (direction) { fittedDirections[direction] *= maxResize.width; }); } if (maxResize.height !== Infinity) { VERTICAL_DIRECTIONS.forEach(function (direction) { fittedDirections[direction] *= maxResize.height; }); } } return fittedDirections; } function distributeOverlap(overlap, first, second) { if (isRoughlyEqual(first + second, 0)) { return overlap / 2; } else if (isRoughlyEqual(first, 0)) { return 0; } else if (isRoughlyEqual(second, 0)) { return overlap; } else { return overlap * Math.abs(first / (first + second)); } } function resizeCoordinatesAlgorithm(coordinates, directions, options, limitations) { var aspectRatio = limitations.aspectRatio, positionRestrictions = limitations.positionRestrictions, sizeRestrictions = limitations.sizeRestrictions; var actualCoordinates = __assign({}, coordinates); directions = __assign({}, directions); var allowedDirections = options.allowedDirections || { left: true, right: true, bottom: true, top: true, }; // It's possible that coordinates can be smaller than minimum width or minimum height. In this case // corresponding resize should be blocked if (coordinates.width < sizeRestrictions.minWidth) { directions.left = 0; directions.right = 0; } if (coordinates.height < sizeRestrictions.minHeight) { directions.top = 0; directions.bottom = 0; } ALL_DIRECTIONS.forEach(function (direction) { if (!allowedDirections[direction]) { directions[direction] = 0; } }); // 1. First step: determine the safe and desired area directions = fitDirections({ coordinates: actualCoordinates, directions: directions, sizeRestrictions: sizeRestrictions, positionRestrictions: positionRestrictions, allowedDirections: allowedDirections, }); // 2. Second step: fix desired box to correspondent to aspect ratio var currentWidth = applyDirections(actualCoordinates, directions).width; var currentHeight = applyDirections(actualCoordinates, directions).height; // Checks ratio: var ratioBroken = options.preserveAspectRatio ? ratio(actualCoordinates) : getBrokenRatio(currentWidth / currentHeight, aspectRatio); if (ratioBroken) { var respectDirection = options.respectDirection; if (respectDirection !== 'width' && respectDirection !== 'height') { if (actualCoordinates.width >= actualCoordinates.height || ratioBroken === 1) { respectDirection = 'width'; } else { respectDirection = 'height'; } } if (respectDirection === 'width') { var overlapHeight = currentWidth / ratioBroken - actualCoordinates.height; if (allowedDirections.top && allowedDirections.bottom) { var top_1 = directions.top, bottom = directions.bottom; directions.bottom = distributeOverlap(overlapHeight, bottom, top_1); directions.top = distributeOverlap(overlapHeight, top_1, bottom); } else if (allowedDirections.bottom) { directions.bottom = overlapHeight; } else if (allowedDirections.top) { directions.top = overlapHeight; } else if (allowedDirections.right) { directions.right = 0; } else if (allowedDirections.left) { directions.left = 0; } } else if (respectDirection === 'height') { var overlapWidth = actualCoordinates.width - currentHeight * ratioBroken; if (allowedDirections.left && allowedDirections.right) { var left = directions.left, right = directions.right; directions.left = -distributeOverlap(overlapWidth, left, right); directions.right = -distributeOverlap(overlapWidth, right, left); } else if (allowedDirections.left) { directions.left = -overlapWidth; } else if (allowedDirections.right) { directions.right = -overlapWidth; } else if (allowedDirections.top) { directions.top = 0; } else if (allowedDirections.bottom) { directions.bottom = 0; } } // 3. Third step: check if desired box with correct aspect ratios break some limits and fit to this conditions directions = fitDirections({ directions: directions, coordinates: actualCoordinates, sizeRestrictions: sizeRestrictions, positionRestrictions: positionRestrictions, preserveRatio: true, compensate: options.compensate, allowedDirections: allowedDirections, }); } // 4. Check if ratio broken (temporary): currentWidth = applyDirections(actualCoordinates, directions).width; currentHeight = applyDirections(actualCoordinates, directions).height; ratioBroken = options.preserveAspectRatio ? ratio(actualCoordinates) : getBrokenRatio(currentWidth / currentHeight, aspectRatio); if (ratioBroken && isGreater(Math.abs(ratioBroken - currentWidth / currentHeight), 0)) { ALL_DIRECTIONS.forEach(function (direction) { directions[direction] = 0; }); } return moveCoordinatesAlgorithm({ width: coordinates.width + directions.right + directions.left, height: coordinates.height + directions.top + directions.bottom, left: coordinates.left, top: coordinates.top, }, { left: -directions.left, top: -directions.top, }, positionRestrictions); } function anchorToMassPoint(coordinates, anchor) { var plainAnchor = anchor.toLowerCase(); return { left: coordinates.left + coordinates.width * (plainAnchor.indexOf('west') !== -1 ? 1 : plainAnchor.indexOf('east') !== -1 ? 0 : 0.5), top: coordinates.top + coordinates.height * (plainAnchor.indexOf('north') !== -1 ? 1 : plainAnchor.indexOf('south') !== -1 ? 0 : 0.5), }; } function anchorToAllowedDirections(anchor) { var plainAnchor = anchor.toLowerCase(); return { left: plainAnchor.indexOf('east') === -1, top: plainAnchor.indexOf('south') === -1, right: plainAnchor.indexOf('west') === -1, bottom: plainAnchor.indexOf('north') === -1, }; } function anchorMoveToResizeDirections(anchor, directions) { var plainAnchor = anchor.toLowerCase(); var normalizedDirections = { left: plainAnchor.indexOf('west') === -1 ? directions.left : -directions.left, top: -directions.top, right: directions.left, bottom: directions.top, }; if (['north', 'south', 'center'].every(function (el) { return plainAnchor.indexOf(el) === -1; })) { normalizedDirections.top = 0; normalizedDirections.bottom = 0; } if (['west', 'east', 'center'].every(function (el) { return plainAnchor.indexOf(el) === -1; })) { normalizedDirections.left = 0; normalizedDirections.right = 0; } var allowedDirections = anchorToAllowedDirections(anchor); ALL_DIRECTIONS.forEach(function (direction) { if (!allowedDirections[direction]) { normalizedDirections[direction] = 0; } }); return normalizedDirections; } function anchoredResizeCoordinatesAlgorithm(coordinates, anchor, directions, options, limitations) { var reference = options.reference; var resizeDirections = anchorMoveToResizeDirections(anchor, directions); var allowedDirections = anchorToAllowedDirections(anchor); var result = resizeCoordinatesAlgorithm(coordinates, resizeDirections, __assign(__assign({}, options), { allowedDirections: allowedDirections }), limitations); if (reference) { // Move the result to anchor result = applyMove(result, diff(anchorToMassPoint(reference, anchor), anchorToMassPoint(result, anchor))); } return moveToPositionRestrictions(result, limitations.positionRestrictions); } export { anchorMoveToResizeDirections, anchorToAllowedDirections, anchoredResizeCoordinatesAlgorithm, fitDirections, resizeCoordinatesAlgorithm };