svelte-motion
Version:
Svelte animation library based on the React library framer-motion.
250 lines (247 loc) • 10.2 kB
JavaScript
/**
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 };