@kcirtaptrick/framer-motion
Version:
A simple and powerful React animation library
302 lines (301 loc) • 13.2 kB
TypeScript
import { Process } from "framesync";
import { AnimationOptions, AnimationPlaybackControls } from "../../animation/animate";
import { ResolvedValues } from "../../render/types";
import { SubscriptionManager } from "../../utils/subscription-manager";
import { Axis, AxisDelta, Box, Delta, Point } from "../geometry/types";
import { NodeStack } from "../shared/stack";
import { IProjectionNode, Layout, LayoutEvents, ProjectionNodeConfig, ProjectionNodeOptions, Snapshot } from "./types";
import { FlatTree } from "../../render/utils/flat-tree";
import { Transition } from "../../types";
import { MotionStyle } from "../../motion/types";
/**
* This should only ever be modified on the client otherwise it'll
* persist through server requests. If we need instanced states we
* could lazy-init via root.
*/
export declare const globalProjectionState: {
/**
* Global flag as to whether the tree has animated since the last time
* we resized the window
*/
hasAnimatedSinceResize: boolean;
/**
* We set this to true once, on the first update. Any nodes added to the tree beyond that
* update will be given a `data-projection-id` attribute.
*/
hasEverUpdated: boolean;
};
export declare function createProjectionNode<I>({ attachResizeListener, defaultParent, measureScroll, resetTransform, }: ProjectionNodeConfig<I>): {
new (id: number | undefined, latestValues?: ResolvedValues, parent?: IProjectionNode | undefined): {
/**
* A unique ID generated for every projection node.
*
* The projection tree's `didUpdate` function will be triggered by the first element
* in the tree to run its layout effects. However, if there are elements entering the tree
* these might not be mounted yet. When React renders a `motion` component we
* give it a unique selector and register it as a potential projection node (not all
* rendered components will be committed by React). In `didUpdate`, we search the DOM for
* these potential nodes with this id and hydrate the projetion node of the ones that were commited.
*/
id: number | undefined;
/**
* A reference to the platform-native node (currently this will be a HTMLElement).
*/
instance: I;
/**
* A reference to the root projection node. There'll only ever be one tree and one root.
*/
root: IProjectionNode;
/**
* A reference to this node's parent.
*/
parent?: IProjectionNode<unknown> | undefined;
/**
* A path from this node to the root node. This provides a fast way to iterate
* back up the tree.
*/
path: IProjectionNode[];
/**
* A Set containing all this component's children. This is used to iterate
* through the children.
*
* TODO: This could be faster to iterate as a flat array stored on the root node.
*/
children: Set<IProjectionNode<unknown>>;
/**
* Options for the node. We use this to configure what kind of layout animations
* we should perform (if any).
*/
options: ProjectionNodeOptions;
/**
* A snapshot of the element's state just before the current update. This is
* hydrated when this node's `willUpdate` method is called and scrubbed at the
* end of the tree's `didUpdate` method.
*/
snapshot: Snapshot | undefined;
/**
* A box defining the element's layout relative to the page. This will have been
* captured with all parent scrolls and projection transforms unset.
*/
layout: Layout | undefined;
/**
* The layout used to calculate the previous layout animation. We use this to compare
* layouts between renders and decide whether we need to trigger a new layout animation
* or just let the current one play out.
*/
targetLayout?: Box | undefined;
/**
* A mutable data structure we use to apply all parent transforms currently
* acting on the element's layout. It's from here we can calculate the projectionDelta
* required to get the element from its layout into its calculated target box.
*/
layoutCorrected: Box;
/**
* An ideal projection transform we want to apply to the element. This is calculated,
* usually when an element's layout has changed, and we want the element to look as though
* its in its previous layout on the next frame. From there, we animated it down to 0
* to animate the element to its new layout.
*/
targetDelta?: Delta | undefined;
/**
* A mutable structure representing the visual bounding box on the page where we want
* and element to appear. This can be set directly but is currently derived once a frame
* from apply targetDelta to layout.
*/
target?: Box | undefined;
/**
* A mutable structure describing a visual bounding box relative to the element's
* projected parent. If defined, target will be derived from this rather than targetDelta.
* If not defined, we'll attempt to calculate on the first layout animation frame
* based on the targets calculated from targetDelta. This will transfer a layout animation
* from viewport-relative to parent-relative.
*/
relativeTarget?: Box | undefined;
relativeTargetOrigin?: Box | undefined;
relativeParent?: IProjectionNode<unknown> | undefined;
/**
* We use this to detect when its safe to shut down part of a projection tree.
* We have to keep projecting children for scale correction and relative projection
* until all their parents stop performing layout animations.
*/
isTreeAnimating: boolean;
isAnimationBlocked: boolean;
/**
* If true, attempt to resolve relativeTarget.
*/
attemptToResolveRelativeTarget?: boolean | undefined;
/**
* A mutable structure that represents the target as transformed by the element's
* latest user-set transforms (ie scale, x)
*/
targetWithTransforms?: Box | undefined;
/**
* A calculated transform that will project an element from its layoutCorrected
* into the target. This will be used by children to calculate their own layoutCorrect boxes.
*/
projectionDelta?: Delta | undefined;
/**
* A calculated transform that will project an element from its layoutCorrected
* into the targetWithTransforms.
*/
projectionDeltaWithTransform?: Delta | undefined;
/**
* If we're tracking the scroll of this element, we store it here.
*/
scroll?: Point | undefined;
/**
* Flag to true if we think this layout has been changed. We can't always know this,
* currently we set it to true every time a component renders, or if it has a layoutDependency
* if that has changed between renders. Additionally, components can be grouped by LayoutGroup
* and if one node is dirtied, they all are.
*/
isLayoutDirty: boolean;
/**
* Block layout updates for instant layout transitions throughout the tree.
*/
updateManuallyBlocked: boolean;
updateBlockedByResize: boolean;
/**
* Set to true between the start of the first `willUpdate` call and the end of the `didUpdate`
* call.
*/
isUpdating: boolean;
/**
* If this is an SVG element we currently disable projection transforms
*/
isSVG: boolean;
/**
* Flag to true (during promotion) if a node doing an instant layout transition needs to reset
* its projection styles.
*/
needsReset: boolean;
/**
* Flags whether this node should have its transform reset prior to measuring.
*/
shouldResetTransform: boolean;
/**
* An object representing the calculated contextual/accumulated/tree scale.
* This will be used to scale calculcated projection transforms, as these are
* calculated in screen-space but need to be scaled for elements to actually
* make it to their calculated destinations.
*
* TODO: Lazy-init
*/
treeScale: Point;
/**
* Is hydrated with a projection node if an element is animating from another.
*/
resumeFrom?: IProjectionNode<unknown> | undefined;
/**
* Is hydrated with a projection node if an element is animating from another.
*/
resumingFrom?: IProjectionNode<unknown> | undefined;
/**
* A reference to the element's latest animated values. This is a reference shared
* between the element's VisualElement and the ProjectionNode.
*/
latestValues: ResolvedValues;
/**
*
*/
eventHandlers: Map<LayoutEvents, SubscriptionManager<any>>;
nodes?: FlatTree | undefined;
depth: number;
/**
* When we update the projection transform, we also build it into a string.
* If the string changes between frames, we trigger a render.
*/
projectionTransform: string;
/**
* If transformTemplate generates a different value before/after the
* update, we need to reset the transform.
*/
prevTransformTemplateValue: string | undefined;
preserveOpacity?: boolean | undefined;
addEventListener(name: LayoutEvents, handler: any): () => void;
notifyListeners(name: LayoutEvents, ...args: any): void;
hasListeners(name: LayoutEvents): boolean;
potentialNodes: Map<number, IProjectionNode<unknown>>;
registerPotentialNode(id: number, node: IProjectionNode): void;
/**
* Lifecycles
*/
mount(instance: I, isLayoutDirty?: boolean): void;
unmount(): void;
blockUpdate(): void;
unblockUpdate(): void;
isUpdateBlocked(): boolean;
isTreeAnimationBlocked(): boolean;
startUpdate(): void;
willUpdate(shouldNotifyListeners?: boolean): void;
didUpdate(): void;
clearAllSnapshots(): void;
scheduleUpdateProjection(): void;
scheduleCheckAfterUnmount(): void;
checkUpdateFailed: () => void;
updateProjection: () => void;
/**
* Update measurements
*/
updateSnapshot(): void;
updateLayout(): void;
updateScroll(): void;
resetTransform(): void;
measure(): Box;
removeElementScroll(box: Box): Box;
applyTransform(box: Box, transformOnly?: boolean): Box;
removeTransform(box: Box): Box;
/**
*
*/
setTargetDelta(delta: Delta): void;
setOptions(options: ProjectionNodeOptions): void;
clearMeasurements(): void;
/**
* Frame calculations
*/
resolveTargetDelta(): void;
getClosestProjectingParent(): IProjectionNode<unknown> | undefined;
hasProjected: boolean;
calcProjection(): void;
isVisible: boolean;
hide(): void;
show(): void;
scheduleRender(notifyAll?: boolean): void;
/**
* Animation
*/
animationValues?: ResolvedValues | undefined;
pendingAnimation?: Process | undefined;
currentAnimation?: AnimationPlaybackControls | undefined;
mixTargetDelta: (progress: number) => void;
animationProgress: number;
setAnimationOrigin(delta: Delta, hasOnlyRelativeTargetChanged?: boolean): void;
startAnimation(options: AnimationOptions<number>): void;
completeAnimation(): void;
finishAnimation(): void;
applyTransformsToTarget(): void;
/**
* Shared layout
*/
sharedNodes: Map<string, NodeStack>;
registerSharedNode(layoutId: string, node: IProjectionNode): void;
isLead(): boolean;
getLead(): IProjectionNode<unknown> | any;
getPrevLead(): IProjectionNode<unknown> | undefined;
getStack(): NodeStack | undefined;
promote({ needsReset, transition, preserveFollowOpacity, }?: {
needsReset?: boolean | undefined;
transition?: Transition | undefined;
preserveFollowOpacity?: boolean | undefined;
}): void;
relegate(): boolean;
resetRotation(): void;
getProjectionStyles(styleProp?: MotionStyle): ResolvedValues;
clearSnapshot(): void;
resetTree(): void;
};
};
export declare function mixAxisDelta(output: AxisDelta, delta: AxisDelta, p: number): void;
export declare function mixAxis(output: Axis, from: Axis, to: Axis, p: number): void;
export declare function mixBox(output: Box, from: Box, to: Box, p: number): void;