UNPKG

framer-motion

Version:

An open source and production-ready motion library for React on the web

241 lines (213 loc) • 6.16 kB
import { Axis, AxisBox2D, BoxDelta, Point2D } from "../../types/geometry" import { mix } from "popmotion" import { ResolvedValues, VisualElement } from "../../render/types" /** * Reset an axis to the provided origin box. * * This is a mutative operation. */ export function resetAxis(axis: Axis, originAxis: Axis) { axis.min = originAxis.min axis.max = originAxis.max } /** * Reset a box to the provided origin box. * * This is a mutative operation. */ export function resetBox(box: AxisBox2D, originBox: AxisBox2D) { resetAxis(box.x, originBox.x) resetAxis(box.y, originBox.y) } /** * Scales a point based on a factor and an originPoint */ export function scalePoint(point: number, scale: number, originPoint: number) { const distanceFromOrigin = point - originPoint const scaled = scale * distanceFromOrigin return originPoint + scaled } /** * Applies a translate/scale delta to a point */ export function applyPointDelta( point: number, translate: number, scale: number, originPoint: number, boxScale?: number ): number { if (boxScale !== undefined) { point = scalePoint(point, boxScale, originPoint) } return scalePoint(point, scale, originPoint) + translate } /** * Applies a translate/scale delta to an axis */ export function applyAxisDelta( axis: Axis, translate: number = 0, scale: number = 1, originPoint: number, boxScale?: number ): void { 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 */ export function applyBoxDelta(box: AxisBox2D, { x, y }: BoxDelta): void { applyAxisDelta(box.x, x.translate, x.scale, x.originPoint) applyAxisDelta(box.y, y.translate, y.scale, y.originPoint) } /** * 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 */ export function applyAxisTransforms( final: Axis, axis: Axis, transforms: ResolvedValues, [key, scaleKey, originKey]: string[] ): void { // Copy the current axis to the final axis before mutation final.min = axis.min final.max = axis.max const axisOrigin = transforms[originKey] !== undefined ? transforms[originKey] : 0.5 const originPoint = mix(axis.min, axis.max, axisOrigin as number) // Apply the axis delta to the final axis applyAxisDelta( final, transforms[key] as number, transforms[scaleKey] as number, originPoint, transforms.scale as number ) } /** * 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. */ export function applyBoxTransforms( finalBox: AxisBox2D, box: AxisBox2D, transforms: ResolvedValues ): void { applyAxisTransforms(finalBox.x, box.x, transforms, xKeys) applyAxisTransforms(finalBox.y, box.y, transforms, yKeys) } /** * Remove a delta from a point. This is essentially the steps of applyPointDelta in reverse */ export function removePointDelta( point: number, translate: number, scale: number, originPoint: number, boxScale?: number ): number { point -= translate point = scalePoint(point, 1 / scale, originPoint) if (boxScale !== undefined) { point = scalePoint(point, 1 / boxScale, originPoint) } return point } /** * Remove a delta from an axis. This is essentially the steps of applyAxisDelta in reverse */ export function removeAxisDelta( axis: Axis, translate: number = 0, scale: number = 1, origin: number = 0.5, boxScale?: number ): void { const originPoint = mix(axis.min, axis.max, origin) - translate axis.min = removePointDelta( axis.min, translate, scale, originPoint, boxScale ) axis.max = removePointDelta( axis.max, translate, scale, originPoint, boxScale ) } /** * Remove a transforms from an axis. This is essentially the steps of applyAxisTransforms in reverse * and acts as a bridge between motion values and removeAxisDelta */ export function removeAxisTransforms( axis: Axis, transforms: ResolvedValues, [key, scaleKey, originKey]: string[] ) { removeAxisDelta( axis, transforms[key] as number, transforms[scaleKey] as number, transforms[originKey] as number, transforms.scale as number ) } /** * Remove a transforms from an box. This is essentially the steps of applyAxisBox in reverse * and acts as a bridge between motion values and removeAxisDelta */ export function removeBoxTransforms( box: AxisBox2D, transforms: ResolvedValues ): void { removeAxisTransforms(box.x, transforms, xKeys) removeAxisTransforms(box.y, transforms, yKeys) } /** * 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 */ export function applyTreeDeltas( box: AxisBox2D, treeScale: Point2D, treePath: VisualElement[] ) { const treeLength = treePath.length if (!treeLength) return // Reset the treeScale treeScale.x = treeScale.y = 1 for (let i = 0; i < treeLength; i++) { const { delta } = treePath[i].getLayoutState() // 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) } }