UNPKG

@kcirtaptrick/framer-motion

Version:

A simple and powerful React animation library

302 lines (301 loc) 13.2 kB
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;