UNPKG

svelte-motion

Version:

Svelte animation library based on the React library framer-motion.

250 lines (247 loc) 10.2 kB
/** based on framer-motion@4.0.3, Copyright (c) 2018 Framer B.V. */ import {fixed} from '../../../utils/fix-process-env'; import { __read } from 'tslib'; import { number, px } from 'style-value-types'; import { isKeyframesTarget } from '../../../animation/utils/is-keyframes-target.js'; //import { invariant } from 'hey-listen'; import { transformProps } from '../../html/utils/transform.js'; import { findDimensionValueType } from '../value-types/dimensions.js'; 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) { var x = _a.x; return x.max - x.min; }, height: function (_a) { var y = _a.y; return y.max - y.min; }, 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, top = elementComputedStyle.top, left = elementComputedStyle.left, bottom = elementComputedStyle.bottom, right = elementComputedStyle.right, transform = elementComputedStyle.transform; var originComputedStyle = { top: top, left: left, bottom: bottom, right: right, transform: transform }; // 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"); } // 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, positionalValues[key](originBbox, originComputedStyle)); 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 = Object.assign({}, target); transitionEnd = Object.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 to = target[key]; var fromType = findDimensionValueType(from); 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; for (var i = to[0] === null ? 1 : 0; 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, unitConversion };