123 lines
4.4 KiB
JavaScript
123 lines
4.4 KiB
JavaScript
import { mix } from '../../utils/mix.mjs';
|
|
import { hasTransform } from '../utils/has-transform.mjs';
|
|
|
|
/**
|
|
* Scales a point based on a factor and an originPoint
|
|
*/
|
|
function scalePoint(point, scale, originPoint) {
|
|
const distanceFromOrigin = point - originPoint;
|
|
const scaled = scale * distanceFromOrigin;
|
|
return originPoint + scaled;
|
|
}
|
|
/**
|
|
* Applies a translate/scale delta to a point
|
|
*/
|
|
function applyPointDelta(point, translate, scale, originPoint, boxScale) {
|
|
if (boxScale !== undefined) {
|
|
point = scalePoint(point, boxScale, originPoint);
|
|
}
|
|
return scalePoint(point, scale, originPoint) + translate;
|
|
}
|
|
/**
|
|
* Applies a translate/scale delta to an axis
|
|
*/
|
|
function applyAxisDelta(axis, translate = 0, scale = 1, originPoint, boxScale) {
|
|
axis.min = applyPointDelta(axis.min, translate, scale, originPoint, boxScale);
|
|
axis.max = applyPointDelta(axis.max, translate, scale, originPoint, boxScale);
|
|
}
|
|
/**
|
|
* Applies a translate/scale delta to a box
|
|
*/
|
|
function applyBoxDelta(box, { x, y }) {
|
|
applyAxisDelta(box.x, x.translate, x.scale, x.originPoint);
|
|
applyAxisDelta(box.y, y.translate, y.scale, y.originPoint);
|
|
}
|
|
/**
|
|
* Apply a tree of deltas to a box. We do this to calculate the effect of all the transforms
|
|
* in a tree upon our box before then calculating how to project it into our desired viewport-relative box
|
|
*
|
|
* This is the final nested loop within updateLayoutDelta for future refactoring
|
|
*/
|
|
function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
|
|
const treeLength = treePath.length;
|
|
if (!treeLength)
|
|
return;
|
|
// Reset the treeScale
|
|
treeScale.x = treeScale.y = 1;
|
|
let node;
|
|
let delta;
|
|
for (let i = 0; i < treeLength; i++) {
|
|
node = treePath[i];
|
|
delta = node.projectionDelta;
|
|
/**
|
|
* TODO: Prefer to remove this, but currently we have motion components with
|
|
* display: contents in Framer.
|
|
*/
|
|
const instance = node.instance;
|
|
if (instance &&
|
|
instance.style &&
|
|
instance.style.display === "contents") {
|
|
continue;
|
|
}
|
|
if (isSharedTransition &&
|
|
node.options.layoutScroll &&
|
|
node.scroll &&
|
|
node !== node.root) {
|
|
transformBox(box, {
|
|
x: -node.scroll.offset.x,
|
|
y: -node.scroll.offset.y,
|
|
});
|
|
}
|
|
if (delta) {
|
|
// Incoporate each ancestor's scale into a culmulative treeScale for this component
|
|
treeScale.x *= delta.x.scale;
|
|
treeScale.y *= delta.y.scale;
|
|
// Apply each ancestor's calculated delta into this component's recorded layout box
|
|
applyBoxDelta(box, delta);
|
|
}
|
|
if (isSharedTransition && hasTransform(node.latestValues)) {
|
|
transformBox(box, node.latestValues);
|
|
}
|
|
}
|
|
/**
|
|
* Snap tree scale back to 1 if it's within a non-perceivable threshold.
|
|
* This will help reduce useless scales getting rendered.
|
|
*/
|
|
treeScale.x = snapToDefault(treeScale.x);
|
|
treeScale.y = snapToDefault(treeScale.y);
|
|
}
|
|
function snapToDefault(scale) {
|
|
if (Number.isInteger(scale))
|
|
return scale;
|
|
return scale > 1.0000000000001 || scale < 0.999999999999 ? scale : 1;
|
|
}
|
|
function translateAxis(axis, distance) {
|
|
axis.min = axis.min + distance;
|
|
axis.max = axis.max + distance;
|
|
}
|
|
/**
|
|
* Apply a transform to an axis from the latest resolved motion values.
|
|
* This function basically acts as a bridge between a flat motion value map
|
|
* and applyAxisDelta
|
|
*/
|
|
function transformAxis(axis, transforms, [key, scaleKey, originKey]) {
|
|
const axisOrigin = transforms[originKey] !== undefined ? transforms[originKey] : 0.5;
|
|
const originPoint = mix(axis.min, axis.max, axisOrigin);
|
|
// Apply the axis delta to the final axis
|
|
applyAxisDelta(axis, transforms[key], transforms[scaleKey], originPoint, transforms.scale);
|
|
}
|
|
/**
|
|
* The names of the motion values we want to apply as translation, scale and origin.
|
|
*/
|
|
const xKeys = ["x", "scaleX", "originX"];
|
|
const yKeys = ["y", "scaleY", "originY"];
|
|
/**
|
|
* Apply a transform to a box from the latest resolved motion values.
|
|
*/
|
|
function transformBox(box, transform) {
|
|
transformAxis(box.x, transform, xKeys);
|
|
transformAxis(box.y, transform, yKeys);
|
|
}
|
|
|
|
export { applyAxisDelta, applyBoxDelta, applyPointDelta, applyTreeDeltas, scalePoint, transformAxis, transformBox, translateAxis };
|