framer-motion
Version:
A simple and powerful JavaScript animation library
580 lines (543 loc) • 21.3 kB
TypeScript
import * as motion_dom from 'motion-dom';
import { AnimationPlaybackControls, TransformProperties, UnresolvedValueKeyframe, Transition, ElementOrSelector, DOMKeyframesDefinition, AnimationOptions, AnimationPlaybackOptions, Easing, AnimationScope, ValueAnimationTransition, EasingFunction, ProgressTimeline, EasingModifier, ValueAnimationOptions, KeyframeGenerator, DynamicOption } from 'motion-dom';
export * from 'motion-dom';
export { BezierDefinition, Easing, EasingDefinition, EasingFunction, EasingModifier, hover, isDragActive, press } from 'motion-dom';
export { invariant, noop, progress } from 'motion-utils';
/**
* @public
*/
type Subscriber<T> = (v: T) => void;
/**
* @public
*/
type PassiveEffect<T> = (v: T, safeSetter: (v: T) => void) => void;
interface MotionValueEventCallbacks<V> {
animationStart: () => void;
animationComplete: () => void;
animationCancel: () => void;
change: (latestValue: V) => void;
renderRequest: () => void;
}
interface ResolvedValues {
[key: string]: string | number;
}
interface Owner {
current: HTMLElement | unknown;
getProps: () => {
onUpdate?: (latest: ResolvedValues) => void;
transformTemplate?: (transform: TransformProperties, generatedTransform: string) => string;
};
}
interface MotionValueOptions {
owner?: Owner;
}
/**
* `MotionValue` is used to track the state and velocity of motion values.
*
* @public
*/
declare class MotionValue<V = any> {
/**
* This will be replaced by the build step with the latest version number.
* When MotionValues are provided to motion components, warn if versions are mixed.
*/
version: string;
/**
* If a MotionValue has an owner, it was created internally within Motion
* and therefore has no external listeners. It is therefore safe to animate via WAAPI.
*/
owner?: Owner;
/**
* The current state of the `MotionValue`.
*/
private current;
/**
* The previous state of the `MotionValue`.
*/
private prev;
/**
* The previous state of the `MotionValue` at the end of the previous frame.
*/
private prevFrameValue;
/**
* The last time the `MotionValue` was updated.
*/
updatedAt: number;
/**
* The time `prevFrameValue` was updated.
*/
prevUpdatedAt: number | undefined;
private stopPassiveEffect?;
/**
* A reference to the currently-controlling animation.
*/
animation?: AnimationPlaybackControls;
setCurrent(current: V): void;
setPrevFrameValue(prevFrameValue?: V | undefined): void;
/**
* Adds a function that will be notified when the `MotionValue` is updated.
*
* It returns a function that, when called, will cancel the subscription.
*
* When calling `onChange` inside a React component, it should be wrapped with the
* `useEffect` hook. As it returns an unsubscribe function, this should be returned
* from the `useEffect` function to ensure you don't add duplicate subscribers..
*
* ```jsx
* export const MyComponent = () => {
* const x = useMotionValue(0)
* const y = useMotionValue(0)
* const opacity = useMotionValue(1)
*
* useEffect(() => {
* function updateOpacity() {
* const maxXY = Math.max(x.get(), y.get())
* const newOpacity = transform(maxXY, [0, 100], [1, 0])
* opacity.set(newOpacity)
* }
*
* const unsubscribeX = x.on("change", updateOpacity)
* const unsubscribeY = y.on("change", updateOpacity)
*
* return () => {
* unsubscribeX()
* unsubscribeY()
* }
* }, [])
*
* return <motion.div style={{ x }} />
* }
* ```
*
* @param subscriber - A function that receives the latest value.
* @returns A function that, when called, will cancel this subscription.
*
* @deprecated
*/
onChange(subscription: Subscriber<V>): () => void;
/**
* An object containing a SubscriptionManager for each active event.
*/
private events;
on<EventName extends keyof MotionValueEventCallbacks<V>>(eventName: EventName, callback: MotionValueEventCallbacks<V>[EventName]): VoidFunction;
clearListeners(): void;
/**
* Sets the state of the `MotionValue`.
*
* @remarks
*
* ```jsx
* const x = useMotionValue(0)
* x.set(10)
* ```
*
* @param latest - Latest value to set.
* @param render - Whether to notify render subscribers. Defaults to `true`
*
* @public
*/
set(v: V, render?: boolean): void;
setWithVelocity(prev: V, current: V, delta: number): void;
/**
* Set the state of the `MotionValue`, stopping any active animations,
* effects, and resets velocity to `0`.
*/
jump(v: V, endAnimation?: boolean): void;
updateAndNotify: (v: V, render?: boolean) => void;
/**
* Returns the latest state of `MotionValue`
*
* @returns - The latest state of `MotionValue`
*
* @public
*/
get(): NonNullable<V>;
/**
* @public
*/
getPrevious(): V | undefined;
/**
* Returns the latest velocity of `MotionValue`
*
* @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
*
* @public
*/
getVelocity(): number;
hasAnimated: boolean;
/**
* Stop the currently active animation.
*
* @public
*/
stop(): void;
/**
* Returns `true` if this value is currently animating.
*
* @public
*/
isAnimating(): boolean;
private clearAnimation;
/**
* Destroy and clean up subscribers to this `MotionValue`.
*
* The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically
* handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
* created a `MotionValue` via the `motionValue` function.
*
* @public
*/
destroy(): void;
}
declare function motionValue<V>(init: V, options?: MotionValueOptions): MotionValue<V>;
interface Point {
x: number;
y: number;
}
type GenericKeyframesTarget<V> = V[] | Array<null | V>;
type ObjectTarget<O> = {
[K in keyof O]?: O[K] | GenericKeyframesTarget<O[K]>;
};
type SequenceTime = number | "<" | `+${number}` | `-${number}` | `${string}`;
type SequenceLabel = string;
interface SequenceLabelWithTime {
name: SequenceLabel;
at: SequenceTime;
}
interface At {
at?: SequenceTime;
}
type MotionValueSegment = [
MotionValue,
UnresolvedValueKeyframe | UnresolvedValueKeyframe[]
];
type MotionValueSegmentWithTransition = [
MotionValue,
UnresolvedValueKeyframe | UnresolvedValueKeyframe[],
Transition & At
];
type DOMSegment = [ElementOrSelector, DOMKeyframesDefinition];
type DOMSegmentWithTransition = [
ElementOrSelector,
DOMKeyframesDefinition,
AnimationOptions & At
];
type ObjectSegment<O extends {} = {}> = [O, ObjectTarget<O>];
type ObjectSegmentWithTransition<O extends {} = {}> = [
O,
ObjectTarget<O>,
AnimationOptions & At
];
type Segment = ObjectSegment | ObjectSegmentWithTransition | SequenceLabel | SequenceLabelWithTime | MotionValueSegment | MotionValueSegmentWithTransition | DOMSegment | DOMSegmentWithTransition;
type AnimationSequence = Segment[];
interface SequenceOptions extends AnimationPlaybackOptions {
delay?: number;
duration?: number;
defaultTransition?: Transition;
}
interface AbsoluteKeyframe {
value: string | number | null;
at: number;
easing?: Easing;
}
type ValueSequence = AbsoluteKeyframe[];
interface SequenceMap {
[key: string]: ValueSequence;
}
type ResolvedAnimationDefinition = {
keyframes: {
[key: string]: UnresolvedValueKeyframe[];
};
transition: {
[key: string]: Transition;
};
};
type ResolvedAnimationDefinitions = Map<Element | MotionValue, ResolvedAnimationDefinition>;
/**
* Creates an animation function that is optionally scoped
* to a specific element.
*/
declare function createScopedAnimate(scope?: AnimationScope): {
(sequence: AnimationSequence, options?: SequenceOptions): AnimationPlaybackControls;
(value: string | MotionValue<string>, keyframes: string | GenericKeyframesTarget<string>, options?: ValueAnimationTransition<string>): AnimationPlaybackControls;
(value: number | MotionValue<number>, keyframes: number | GenericKeyframesTarget<number>, options?: ValueAnimationTransition<number>): AnimationPlaybackControls;
<V>(value: V | MotionValue<V>, keyframes: V | GenericKeyframesTarget<V>, options?: ValueAnimationTransition<V>): AnimationPlaybackControls;
(element: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions): AnimationPlaybackControls;
<O extends {}>(object: O | O[], keyframes: ObjectTarget<O>, options?: AnimationOptions): AnimationPlaybackControls;
};
declare const animate: {
(sequence: AnimationSequence, options?: SequenceOptions): AnimationPlaybackControls;
(value: string | MotionValue<string>, keyframes: string | GenericKeyframesTarget<string>, options?: ValueAnimationTransition<string>): AnimationPlaybackControls;
(value: number | MotionValue<number>, keyframes: number | GenericKeyframesTarget<number>, options?: ValueAnimationTransition<number>): AnimationPlaybackControls;
<V>(value: V | MotionValue<V>, keyframes: V | GenericKeyframesTarget<V>, options?: ValueAnimationTransition<V>): AnimationPlaybackControls;
(element: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions): AnimationPlaybackControls;
<O extends {}>(object: O | O[], keyframes: ObjectTarget<O>, options?: AnimationOptions): AnimationPlaybackControls;
};
declare const animateMini: (elementOrSelector: ElementOrSelector, keyframes: DOMKeyframesDefinition, options?: AnimationOptions) => AnimationPlaybackControls;
interface ScrollOptions {
source?: HTMLElement;
container?: HTMLElement;
target?: Element;
axis?: "x" | "y";
offset?: ScrollOffset;
}
type OnScrollProgress = (progress: number) => void;
type OnScrollWithInfo = (progress: number, info: ScrollInfo) => void;
type OnScroll = OnScrollProgress | OnScrollWithInfo;
interface AxisScrollInfo {
current: number;
offset: number[];
progress: number;
scrollLength: number;
velocity: number;
targetOffset: number;
targetLength: number;
containerLength: number;
interpolatorOffsets?: number[];
interpolate?: EasingFunction;
}
interface ScrollInfo {
time: number;
x: AxisScrollInfo;
y: AxisScrollInfo;
}
type OnScrollInfo = (info: ScrollInfo) => void;
type SupportedEdgeUnit = "px" | "vw" | "vh" | "%";
type EdgeUnit = `${number}${SupportedEdgeUnit}`;
type NamedEdges = "start" | "end" | "center";
type EdgeString = NamedEdges | EdgeUnit | `${number}`;
type Edge = EdgeString | number;
type ProgressIntersection = [number, number];
type Intersection = `${Edge} ${Edge}`;
type ScrollOffset = Array<Edge | Intersection | ProgressIntersection>;
interface ScrollInfoOptions {
container?: HTMLElement;
target?: Element;
axis?: "x" | "y";
offset?: ScrollOffset;
}
declare global {
interface Window {
ScrollTimeline: ScrollTimeline;
}
}
declare class ScrollTimeline implements ProgressTimeline {
constructor(options: ScrollOptions);
currentTime: null | {
value: number;
};
cancel?: VoidFunction;
}
declare function scroll(onScroll: OnScroll | AnimationPlaybackControls, { axis, ...options }?: ScrollOptions): VoidFunction;
declare function scrollInfo(onScroll: OnScrollInfo, { container, ...options }?: ScrollInfoOptions): () => void;
type ViewChangeHandler = (entry: IntersectionObserverEntry) => void;
type MarginValue = `${number}${"px" | "%"}`;
type MarginType = MarginValue | `${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue}` | `${MarginValue} ${MarginValue} ${MarginValue} ${MarginValue}`;
interface InViewOptions {
root?: Element | Document;
margin?: MarginType;
amount?: "some" | "all" | number;
}
declare function inView(elementOrSelector: ElementOrSelector, onStart: (element: Element, entry: IntersectionObserverEntry) => void | ViewChangeHandler, { root, margin: rootMargin, amount }?: InViewOptions): VoidFunction;
declare const anticipate: (p: number) => number;
declare const backOut: (t: number) => number;
declare const backIn: motion_dom.EasingFunction;
declare const backInOut: motion_dom.EasingFunction;
declare const circIn: EasingFunction;
declare const circOut: EasingFunction;
declare const circInOut: EasingFunction;
declare function cubicBezier(mX1: number, mY1: number, mX2: number, mY2: number): (t: number) => number;
declare const easeIn: (t: number) => number;
declare const easeOut: (t: number) => number;
declare const easeInOut: (t: number) => number;
declare const mirrorEasing: EasingModifier;
declare const reverseEasing: EasingModifier;
type Direction = "start" | "end";
declare function steps(numSteps: number, direction?: Direction): EasingFunction;
declare function inertia({ keyframes, velocity, power, timeConstant, bounceDamping, bounceStiffness, modifyTarget, min, max, restDelta, restSpeed, }: ValueAnimationOptions<number>): KeyframeGenerator<number>;
declare function keyframes<T extends string | number>({ duration, keyframes: keyframeValues, times, ease, }: ValueAnimationOptions<T>): KeyframeGenerator<T>;
declare function spring(optionsOrVisualDuration?: ValueAnimationOptions<number> | number, bounce?: number): KeyframeGenerator<number>;
type StaggerOrigin = "first" | "last" | "center" | number;
type StaggerOptions = {
startDelay?: number;
from?: StaggerOrigin;
ease?: Easing;
};
declare function stagger(duration?: number, { startDelay, from, ease }?: StaggerOptions): DynamicOption<number>;
type Process = (data: FrameData) => void;
type Schedule = (process: Process, keepAlive?: boolean, immediate?: boolean) => Process;
interface Step {
schedule: Schedule;
cancel: (process: Process) => void;
process: (data: FrameData) => void;
}
type StepId = "read" | "resolveKeyframes" | "update" | "preRender" | "render" | "postRender";
type Batcher = {
[key in StepId]: Schedule;
};
type Steps = {
[key in StepId]: Step;
};
interface FrameData {
delta: number;
timestamp: number;
isProcessing: boolean;
}
declare const frame: Batcher;
declare const cancelFrame: (process: Process) => void;
declare const frameData: FrameData;
declare const frameSteps: Steps;
/**
* An eventloop-synchronous alternative to performance.now().
*
* Ensures that time measurements remain consistent within a synchronous context.
* Usually calling performance.now() twice within the same synchronous context
* will return different values which isn't useful for animations when we're usually
* trying to sync animations to the same frame.
*/
declare const time: {
now: () => number;
set: (newTime: number) => void;
};
declare const clamp: (min: number, max: number, v: number) => number;
type DelayedFunction = (overshoot: number) => void;
declare function delayInSeconds(callback: DelayedFunction, timeout: number): () => void;
declare const distance: (a: number, b: number) => number;
declare function distance2D(a: Point, b: Point): number;
type Mix<T> = (v: number) => T;
type MixerFactory<T> = (from: T, to: T) => Mix<T>;
interface InterpolateOptions<T> {
clamp?: boolean;
ease?: EasingFunction | EasingFunction[];
mixer?: MixerFactory<T>;
}
/**
* Create a function that maps from a numerical input array to a generic output array.
*
* Accepts:
* - Numbers
* - Colors (hex, hsl, hsla, rgb, rgba)
* - Complex (combinations of one or more numbers or strings)
*
* ```jsx
* const mixColor = interpolate([0, 1], ['#fff', '#000'])
*
* mixColor(0.5) // 'rgba(128, 128, 128, 1)'
* ```
*
* TODO Revist this approach once we've moved to data models for values,
* probably not needed to pregenerate mixer functions.
*
* @public
*/
declare function interpolate<T>(input: number[], output: T[], { clamp: isClamp, ease, mixer }?: InterpolateOptions<T>): (v: number) => T;
type Mixer<T> = (p: number) => T;
declare function mix<T>(from: T, to: T): Mixer<T>;
declare function mix(from: number, to: number, p: number): number;
declare const pipe: (...transformers: Function[]) => Function;
/**
* @public
*/
interface TransformOptions<T> {
/**
* Clamp values to within the given range. Defaults to `true`
*
* @public
*/
clamp?: boolean;
/**
* Easing functions to use on the interpolations between each value in the input and output ranges.
*
* If provided as an array, the array must be one item shorter than the input and output ranges, as the easings apply to the transition **between** each.
*
* @public
*/
ease?: EasingFunction | EasingFunction[];
/**
* Provide a function that can interpolate between any two values in the provided range.
*
* @public
*/
mixer?: (from: T, to: T) => (v: number) => any;
}
/**
* Transforms numbers into other values by mapping them from an input range to an output range.
* Returns the type of the input provided.
*
* @remarks
*
* Given an input range of `[0, 200]` and an output range of
* `[0, 1]`, this function will return a value between `0` and `1`.
* The input range must be a linear series of numbers. The output range
* can be any supported value type, such as numbers, colors, shadows, arrays, objects and more.
* Every value in the output range must be of the same type and in the same format.
*
* ```jsx
* import * as React from "react"
* import { transform } from "framer-motion"
*
* export function MyComponent() {
* const inputRange = [0, 200]
* const outputRange = [0, 1]
* const output = transform(100, inputRange, outputRange)
*
* // Returns 0.5
* return <div>{output}</div>
* }
* ```
*
* @param inputValue - A number to transform between the input and output ranges.
* @param inputRange - A linear series of numbers (either all increasing or decreasing).
* @param outputRange - A series of numbers, colors, strings, or arrays/objects of those. Must be the same length as `inputRange`.
* @param options - Clamp: Clamp values to within the given range. Defaults to `true`.
*
* @public
*/
declare function transform<T>(inputValue: number, inputRange: number[], outputRange: T[], options?: TransformOptions<T>): T;
/**
*
* Transforms numbers into other values by mapping them from an input range to an output range.
*
* Given an input range of `[0, 200]` and an output range of
* `[0, 1]`, this function will return a value between `0` and `1`.
* The input range must be a linear series of numbers. The output range
* can be any supported value type, such as numbers, colors, shadows, arrays, objects and more.
* Every value in the output range must be of the same type and in the same format.
*
* ```jsx
* import * as React from "react"
* import { Frame, transform } from "framer"
*
* export function MyComponent() {
* const inputRange = [-200, -100, 100, 200]
* const outputRange = [0, 1, 1, 0]
* const convertRange = transform(inputRange, outputRange)
* const output = convertRange(-150)
*
* // Returns 0.5
* return <div>{output}</div>
* }
*
* ```
*
* @param inputRange - A linear series of numbers (either all increasing or decreasing).
* @param outputRange - A series of numbers, colors or strings. Must be the same length as `inputRange`.
* @param options - Clamp: clamp values to within the given range. Defaults to `true`.
*
* @public
*/
declare function transform<T>(inputRange: number[], outputRange: T[], options?: TransformOptions<T>): (inputValue: number) => T;
declare const wrap: (min: number, max: number, v: number) => number;
/**
* @deprecated
*
* Import as `frame` instead.
*/
declare const sync: Batcher;
/**
* @deprecated
*
* Use cancelFrame(callback) instead.
*/
declare const cancelSync: Record<string, (process: Process) => void>;
export { type AbsoluteKeyframe, type AnimationSequence, type At, type DOMSegment, type DOMSegmentWithTransition, type DelayedFunction, type Direction, type InterpolateOptions, type MixerFactory, MotionValue, type MotionValueSegment, type MotionValueSegmentWithTransition, type ObjectSegment, type ObjectSegmentWithTransition, type ObjectTarget, type PassiveEffect, type ResolvedAnimationDefinition, type ResolvedAnimationDefinitions, type Segment, type SequenceLabel, type SequenceLabelWithTime, type SequenceMap, type SequenceOptions, type SequenceTime, type Subscriber, type ValueSequence, animate, animateMini, anticipate, backIn, backInOut, backOut, cancelFrame, cancelSync, circIn, circInOut, circOut, clamp, createScopedAnimate, cubicBezier, delayInSeconds as delay, distance, distance2D, easeIn, easeInOut, easeOut, frame, frameData, frameSteps, inView, inertia, interpolate, keyframes, mirrorEasing, mix, motionValue, pipe, reverseEasing, scroll, scrollInfo, spring, stagger, steps, sync, time, transform, wrap };