UNPKG

framer-motion

Version:

A simple and powerful React animation library

259 lines (256 loc) • 10.6 kB
import { __assign, __read } from 'tslib'; import { number, px } from 'style-value-types'; import { isKeyframesTarget } from '../../../animation/utils/is-keyframes-target.mjs'; import { invariant } from 'hey-listen'; import { transformProps } from '../../html/utils/transform.mjs'; import { findDimensionValueType } from '../value-types/dimensions.mjs'; var positionalKeys = new Set([ "width", "height", "top", "left", "right", "bottom", "x", "y", ]); var isPositionalKey = function (key) { return positionalKeys.has(key); }; var hasPositionalKey = function (target) { return Object.keys(target).some(isPositionalKey); }; var setAndResetVelocity = function (value, to) { // Looks odd but setting it twice doesn't render, it'll just // set both prev and current to the latest value value.set(to, false); value.set(to); }; var isNumOrPxType = function (v) { return v === number || v === px; }; var BoundingBoxDimension; (function (BoundingBoxDimension) { BoundingBoxDimension["width"] = "width"; BoundingBoxDimension["height"] = "height"; BoundingBoxDimension["left"] = "left"; BoundingBoxDimension["right"] = "right"; BoundingBoxDimension["top"] = "top"; BoundingBoxDimension["bottom"] = "bottom"; })(BoundingBoxDimension || (BoundingBoxDimension = {})); var getPosFromMatrix = function (matrix, pos) { return parseFloat(matrix.split(", ")[pos]); }; var getTranslateFromMatrix = function (pos2, pos3) { return function (_bbox, _a) { var transform = _a.transform; if (transform === "none" || !transform) return 0; var matrix3d = transform.match(/^matrix3d\((.+)\)$/); if (matrix3d) { return getPosFromMatrix(matrix3d[1], pos3); } else { var matrix = transform.match(/^matrix\((.+)\)$/); if (matrix) { return getPosFromMatrix(matrix[1], pos2); } else { return 0; } } }; }; var transformKeys = new Set(["x", "y", "z"]); var nonTranslationalTransformKeys = transformProps.filter(function (key) { return !transformKeys.has(key); }); function removeNonTranslationalTransform(visualElement) { var removedTransforms = []; nonTranslationalTransformKeys.forEach(function (key) { var value = visualElement.getValue(key); if (value !== undefined) { removedTransforms.push([key, value.get()]); value.set(key.startsWith("scale") ? 1 : 0); } }); // Apply changes to element before measurement if (removedTransforms.length) visualElement.syncRender(); return removedTransforms; } var positionalValues = { // Dimensions width: function (_a, _b) { var x = _a.x; var _c = _b.paddingLeft, paddingLeft = _c === void 0 ? "0" : _c, _d = _b.paddingRight, paddingRight = _d === void 0 ? "0" : _d; return x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight); }, height: function (_a, _b) { var y = _a.y; var _c = _b.paddingTop, paddingTop = _c === void 0 ? "0" : _c, _d = _b.paddingBottom, paddingBottom = _d === void 0 ? "0" : _d; return y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom); }, top: function (_bbox, _a) { var top = _a.top; return parseFloat(top); }, left: function (_bbox, _a) { var left = _a.left; return parseFloat(left); }, bottom: function (_a, _b) { var y = _a.y; var top = _b.top; return parseFloat(top) + (y.max - y.min); }, right: function (_a, _b) { var x = _a.x; var left = _b.left; return parseFloat(left) + (x.max - x.min); }, // Transform x: getTranslateFromMatrix(4, 13), y: getTranslateFromMatrix(5, 14), }; var convertChangedValueTypes = function (target, visualElement, changedKeys) { var originBbox = visualElement.measureViewportBox(); var element = visualElement.getInstance(); var elementComputedStyle = getComputedStyle(element); var display = elementComputedStyle.display; var origin = {}; // If the element is currently set to display: "none", make it visible before // measuring the target bounding box if (display === "none") { visualElement.setStaticValue("display", target.display || "block"); } /** * Record origins before we render and update styles */ changedKeys.forEach(function (key) { origin[key] = positionalValues[key](originBbox, elementComputedStyle); }); // Apply the latest values (as set in checkAndConvertChangedValueTypes) visualElement.syncRender(); var targetBbox = visualElement.measureViewportBox(); changedKeys.forEach(function (key) { // Restore styles to their **calculated computed style**, not their actual // originally set style. This allows us to animate between equivalent pixel units. var value = visualElement.getValue(key); setAndResetVelocity(value, origin[key]); target[key] = positionalValues[key](targetBbox, elementComputedStyle); }); return target; }; var checkAndConvertChangedValueTypes = function (visualElement, target, origin, transitionEnd) { if (origin === void 0) { origin = {}; } if (transitionEnd === void 0) { transitionEnd = {}; } target = __assign({}, target); transitionEnd = __assign({}, transitionEnd); var targetPositionalKeys = Object.keys(target).filter(isPositionalKey); // We want to remove any transform values that could affect the element's bounding box before // it's measured. We'll reapply these later. var removedTransformValues = []; var hasAttemptedToRemoveTransformValues = false; var changedValueTypeKeys = []; targetPositionalKeys.forEach(function (key) { var value = visualElement.getValue(key); if (!visualElement.hasValue(key)) return; var from = origin[key]; var fromType = findDimensionValueType(from); var to = target[key]; var toType; // TODO: The current implementation of this basically throws an error // if you try and do value conversion via keyframes. There's probably // a way of doing this but the performance implications would need greater scrutiny, // as it'd be doing multiple resize-remeasure operations. if (isKeyframesTarget(to)) { var numKeyframes = to.length; var fromIndex = to[0] === null ? 1 : 0; from = to[fromIndex]; fromType = findDimensionValueType(from); for (var i = fromIndex; i < numKeyframes; i++) { if (!toType) { toType = findDimensionValueType(to[i]); invariant(toType === fromType || (isNumOrPxType(fromType) && isNumOrPxType(toType)), "Keyframes must be of the same dimension as the current value"); } else { invariant(findDimensionValueType(to[i]) === toType, "All keyframes must be of the same type"); } } } else { toType = findDimensionValueType(to); } if (fromType !== toType) { // If they're both just number or px, convert them both to numbers rather than // relying on resize/remeasure to convert (which is wasteful in this situation) if (isNumOrPxType(fromType) && isNumOrPxType(toType)) { var current = value.get(); if (typeof current === "string") { value.set(parseFloat(current)); } if (typeof to === "string") { target[key] = parseFloat(to); } else if (Array.isArray(to) && toType === px) { target[key] = to.map(parseFloat); } } else if ((fromType === null || fromType === void 0 ? void 0 : fromType.transform) && (toType === null || toType === void 0 ? void 0 : toType.transform) && (from === 0 || to === 0)) { // If one or the other value is 0, it's safe to coerce it to the // type of the other without measurement if (from === 0) { value.set(toType.transform(from)); } else { target[key] = fromType.transform(to); } } else { // If we're going to do value conversion via DOM measurements, we first // need to remove non-positional transform values that could affect the bbox measurements. if (!hasAttemptedToRemoveTransformValues) { removedTransformValues = removeNonTranslationalTransform(visualElement); hasAttemptedToRemoveTransformValues = true; } changedValueTypeKeys.push(key); transitionEnd[key] = transitionEnd[key] !== undefined ? transitionEnd[key] : target[key]; setAndResetVelocity(value, to); } } }); if (changedValueTypeKeys.length) { var convertedTarget = convertChangedValueTypes(target, visualElement, changedValueTypeKeys); // If we removed transform values, reapply them before the next render if (removedTransformValues.length) { removedTransformValues.forEach(function (_a) { var _b = __read(_a, 2), key = _b[0], value = _b[1]; visualElement.getValue(key).set(value); }); } // Reapply original values visualElement.syncRender(); return { target: convertedTarget, transitionEnd: transitionEnd }; } else { return { target: target, transitionEnd: transitionEnd }; } }; /** * Convert value types for x/y/width/height/top/left/bottom/right * * Allows animation between `'auto'` -> `'100%'` or `0` -> `'calc(50% - 10vw)'` * * @internal */ function unitConversion(visualElement, target, origin, transitionEnd) { return hasPositionalKey(target) ? checkAndConvertChangedValueTypes(visualElement, target, origin, transitionEnd) : { target: target, transitionEnd: transitionEnd }; } export { BoundingBoxDimension, positionalValues, unitConversion };