framer-motion
Version:
A simple and powerful React animation library
1,694 lines (1,677 loc) • 151 kB
TypeScript
/// <reference types="react" />
import * as React$1 from 'react';
import { RefObject, CSSProperties, SVGAttributes, ForwardRefExoticComponent, PropsWithoutRef, RefAttributes, ReactHTML, DetailedHTMLFactory, HTMLAttributes, useEffect } from 'react';
import { Easing as Easing$1, SpringOptions } from 'popmotion';
import { ScrollOptions, InViewOptions } from '@motionone/dom';
/**
* @public
*/
declare type Subscriber<T> = (v: T) => void;
/**
* @public
*/
declare type PassiveEffect<T> = (v: T, safeSetter: (v: T) => void) => void;
/**
* `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;
/**
* 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.onChange(updateOpacity)
* const unsubscribeY = y.onChange(updateOpacity)
*
* return () => {
* unsubscribeX()
* unsubscribeY()
* }
* }, [])
*
* return <motion.div style={{ x }} />
* }
* ```
*
* @privateRemarks
*
* We could look into a `useOnChange` hook if the above lifecycle management proves confusing.
*
* ```jsx
* useOnChange(x, () => {})
* ```
*
* @param subscriber - A function that receives the latest value.
* @returns A function that, when called, will cancel this subscription.
*
* @public
*/
onChange(subscription: Subscriber<V>): () => void;
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;
updateAndNotify: (v: V, render?: boolean) => void;
/**
* Returns the latest state of `MotionValue`
*
* @returns - The latest state of `MotionValue`
*
* @public
*/
get(): V;
/**
* @public
*/
getPrevious(): V;
/**
* 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): MotionValue<V>;
/**
* @public
*/
declare type ControlsAnimationDefinition = string | string[] | TargetAndTransition | TargetResolver;
/**
* @public
*/
interface AnimationControls {
/**
* Starts an animation on all linked components.
*
* @remarks
*
* ```jsx
* controls.start("variantLabel")
* controls.start({
* x: 0,
* transition: { duration: 1 }
* })
* ```
*
* @param definition - Properties or variant label to animate to
* @param transition - Optional `transtion` to apply to a variant
* @returns - A `Promise` that resolves when all animations have completed.
*
* @public
*/
start(definition: ControlsAnimationDefinition, transitionOverride?: Transition): Promise<any>;
/**
* Instantly set to a set of properties or a variant.
*
* ```jsx
* // With properties
* controls.set({ opacity: 0 })
*
* // With variants
* controls.set("hidden")
* ```
*
* @privateRemarks
* We could perform a similar trick to `.start` where this can be called before mount
* and we maintain a list of of pending actions that get applied on mount. But the
* expectation of `set` is that it happens synchronously and this would be difficult
* to do before any children have even attached themselves. It's also poor practise
* and we should discourage render-synchronous `.start` calls rather than lean into this.
*
* @public
*/
set(definition: ControlsAnimationDefinition): void;
/**
* Stops animations on all linked components.
*
* ```jsx
* controls.stop()
* ```
*
* @public
*/
stop(): void;
mount(): () => void;
}
interface Point {
x: number;
y: number;
}
interface Axis {
min: number;
max: number;
}
interface Box {
x: Axis;
y: Axis;
}
interface BoundingBox {
top: number;
right: number;
bottom: number;
left: number;
}
interface AxisDelta {
translate: number;
scale: number;
origin: number;
originPoint: number;
}
interface Delta {
x: AxisDelta;
y: AxisDelta;
}
declare type TransformPoint = (point: Point) => Point;
/**
* Passed in to pan event handlers like `onPan` the `PanInfo` object contains
* information about the current state of the tap gesture such as its
* `point`, `delta`, `offset` and `velocity`.
*
* ```jsx
* <motion.div onPan={(event, info) => {
* console.log(info.point.x, info.point.y)
* }} />
* ```
*
* @public
*/
interface PanInfo {
/**
* Contains `x` and `y` values for the current pan position relative
* to the device or page.
*
* ```jsx
* function onPan(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onPan={onPan} />
* ```
*
* @public
*/
point: Point;
/**
* Contains `x` and `y` values for the distance moved since
* the last event.
*
* ```jsx
* function onPan(event, info) {
* console.log(info.delta.x, info.delta.y)
* }
*
* <motion.div onPan={onPan} />
* ```
*
* @public
*/
delta: Point;
/**
* Contains `x` and `y` values for the distance moved from
* the first pan event.
*
* ```jsx
* function onPan(event, info) {
* console.log(info.offset.x, info.offset.y)
* }
*
* <motion.div onPan={onPan} />
* ```
*
* @public
*/
offset: Point;
/**
* Contains `x` and `y` values for the current velocity of the pointer, in px/ms.
*
* ```jsx
* function onPan(event, info) {
* console.log(info.velocity.x, info.velocity.y)
* }
*
* <motion.div onPan={onPan} />
* ```
*
* @public
*/
velocity: Point;
}
interface DragControlOptions {
snapToCursor?: boolean;
cursorProgress?: Point;
}
/**
* Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components.
*
* ```jsx
* const dragControls = useDragControls()
*
* function startDrag(event) {
* dragControls.start(event, { snapToCursor: true })
* }
*
* return (
* <>
* <div onPointerDown={startDrag} />
* <motion.div drag="x" dragControls={dragControls} />
* </>
* )
* ```
*
* @public
*/
declare class DragControls {
private componentControls;
/**
* Start a drag gesture on every `motion` component that has this set of drag controls
* passed into it via the `dragControls` prop.
*
* ```jsx
* dragControls.start(e, {
* snapToCursor: true
* })
* ```
*
* @param event - PointerEvent
* @param options - Options
*
* @public
*/
start(event: React$1.MouseEvent | React$1.TouchEvent | React$1.PointerEvent | MouseEvent | TouchEvent | PointerEvent, options?: DragControlOptions): void;
}
/**
* Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop
* and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we
* might want to initiate that dragging from a different component than the draggable one.
*
* By creating a `dragControls` using the `useDragControls` hook, we can pass this into
* the draggable component's `dragControls` prop. It exposes a `start` method
* that can start dragging from pointer events on other components.
*
* ```jsx
* const dragControls = useDragControls()
*
* function startDrag(event) {
* dragControls.start(event, { snapToCursor: true })
* }
*
* return (
* <>
* <div onPointerDown={startDrag} />
* <motion.div drag="x" dragControls={dragControls} />
* </>
* )
* ```
*
* @public
*/
declare function useDragControls(): DragControls;
declare type DragElastic = boolean | number | Partial<BoundingBox>;
/**
* @public
*/
interface DragHandlers {
/**
* Callback function that fires when dragging starts.
*
* ```jsx
* <motion.div
* drag
* onDragStart={
* (event, info) => console.log(info.point.x, info.point.y)
* }
* />
* ```
*
* @public
*/
onDragStart?(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void;
/**
* Callback function that fires when dragging ends.
*
* ```jsx
* <motion.div
* drag
* onDragEnd={
* (event, info) => console.log(info.point.x, info.point.y)
* }
* />
* ```
*
* @public
*/
onDragEnd?(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void;
/**
* Callback function that fires when the component is dragged.
*
* ```jsx
* <motion.div
* drag
* onDrag={
* (event, info) => console.log(info.point.x, info.point.y)
* }
* />
* ```
*
* @public
*/
onDrag?(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void;
/**
* Callback function that fires a drag direction is determined.
*
* ```jsx
* <motion.div
* drag
* dragDirectionLock
* onDirectionLock={axis => console.log(axis)}
* />
* ```
*
* @public
*/
onDirectionLock?(axis: "x" | "y"): void;
/**
* Callback function that fires when drag momentum/bounce transition finishes.
*
* ```jsx
* <motion.div
* drag
* onDragTransitionEnd={() => console.log('Drag transition complete')}
* />
* ```
*
* @public
*/
onDragTransitionEnd?(): void;
}
/**
* @public
*/
declare type InertiaOptions = Partial<Omit<Inertia, "velocity" | "type">>;
/**
* @public
*/
interface DraggableProps extends DragHandlers {
/**
* Enable dragging for this element. Set to `false` by default.
* Set `true` to drag in both directions.
* Set `"x"` or `"y"` to only drag in a specific direction.
*
* ```jsx
* <motion.div drag="x" />
* ```
*/
drag?: boolean | "x" | "y";
/**
* Properties or variant label to animate to while the drag gesture is recognised.
*
* ```jsx
* <motion.div whileDrag={{ scale: 1.2 }} />
* ```
*/
whileDrag?: VariantLabels | TargetAndTransition;
/**
* If `true`, this will lock dragging to the initially-detected direction. Defaults to `false`.
*
* ```jsx
* <motion.div drag dragDirectionLock />
* ```
*/
dragDirectionLock?: boolean;
/**
* Allows drag gesture propagation to child components. Set to `false` by
* default.
*
* ```jsx
* <motion.div drag="x" dragPropagation />
* ```
*/
dragPropagation?: boolean;
/**
* Applies constraints on the permitted draggable area.
*
* It can accept an object of optional `top`, `left`, `right`, and `bottom` values, measured in pixels.
* This will define a distance the named edge of the draggable component.
*
* Alternatively, it can accept a `ref` to another component created with React's `useRef` hook.
* This `ref` should be passed both to the draggable component's `dragConstraints` prop, and the `ref`
* of the component you want to use as constraints.
*
* ```jsx
* // In pixels
* <motion.div
* drag="x"
* dragConstraints={{ left: 0, right: 300 }}
* />
*
* // As a ref to another component
* const MyComponent = () => {
* const constraintsRef = useRef(null)
*
* return (
* <motion.div ref={constraintsRef}>
* <motion.div drag dragConstraints={constraintsRef} />
* </motion.div>
* )
* }
* ```
*/
dragConstraints?: false | Partial<BoundingBox> | RefObject<Element>;
/**
* The degree of movement allowed outside constraints. 0 = no movement, 1 =
* full movement.
*
* Set to `0.5` by default. Can also be set as `false` to disable movement.
*
* By passing an object of `top`/`right`/`bottom`/`left`, individual values can be set
* per constraint. Any missing values will be set to `0`.
*
* ```jsx
* <motion.div
* drag
* dragConstraints={{ left: 0, right: 300 }}
* dragElastic={0.2}
* />
* ```
*/
dragElastic?: DragElastic;
/**
* Apply momentum from the pan gesture to the component when dragging
* finishes. Set to `true` by default.
*
* ```jsx
* <motion.div
* drag
* dragConstraints={{ left: 0, right: 300 }}
* dragMomentum={false}
* />
* ```
*/
dragMomentum?: boolean;
/**
* Allows you to change dragging inertia parameters.
* When releasing a draggable Frame, an animation with type `inertia` starts. The animation is based on your dragging velocity. This property allows you to customize it.
* See {@link https://framer.com/api/animation/#inertia | Inertia} for all properties you can use.
*
* ```jsx
* <motion.div
* drag
* dragTransition={{ bounceStiffness: 600, bounceDamping: 10 }}
* />
* ```
*/
dragTransition?: InertiaOptions;
/**
* Usually, dragging is initiated by pressing down on a component and moving it. For some
* use-cases, for instance clicking at an arbitrary point on a video scrubber, we
* might want to initiate dragging from a different component than the draggable one.
*
* By creating a `dragControls` using the `useDragControls` hook, we can pass this into
* the draggable component's `dragControls` prop. It exposes a `start` method
* that can start dragging from pointer events on other components.
*
* ```jsx
* const dragControls = useDragControls()
*
* function startDrag(event) {
* dragControls.start(event, { snapToCursor: true })
* }
*
* return (
* <>
* <div onPointerDown={startDrag} />
* <motion.div drag="x" dragControls={dragControls} />
* </>
* )
* ```
*/
dragControls?: DragControls;
/**
* If true, element will snap back to its origin when dragging ends.
*
* Enabling this is the equivalent of setting all `dragConstraints` axes to `0`
* with `dragElastic={1}`, but when used together `dragConstraints` can define
* a wider draggable area and `dragSnapToOrigin` will ensure the element
* animates back to its origin on release.
*/
dragSnapToOrigin?: boolean;
/**
* By default, if `drag` is defined on a component then an event listener will be attached
* to automatically initiate dragging when a user presses down on it.
*
* By setting `dragListener` to `false`, this event listener will not be created.
*
* ```jsx
* const dragControls = useDragControls()
*
* function startDrag(event) {
* dragControls.start(event, { snapToCursor: true })
* }
*
* return (
* <>
* <div onPointerDown={startDrag} />
* <motion.div
* drag="x"
* dragControls={dragControls}
* dragListener={false}
* />
* </>
* )
* ```
*/
dragListener?: boolean;
/**
* If `dragConstraints` is set to a React ref, this callback will call with the measured drag constraints.
*
* @public
*/
onMeasureDragConstraints?: (constraints: BoundingBox) => BoundingBox | void;
/**
* Usually, dragging uses the layout project engine, and applies transforms to the underlying VisualElement.
* Passing MotionValues as _dragX and _dragY instead applies drag updates to these motion values.
* This allows you to manually control how updates from a drag gesture on an element is applied.
*
* @public
*/
_dragX?: MotionValue<number>;
/**
* Usually, dragging uses the layout project engine, and applies transforms to the underlying VisualElement.
* Passing MotionValues as _dragX and _dragY instead applies drag updates to these motion values.
* This allows you to manually control how updates from a drag gesture on an element is applied.
*
* @public
*/
_dragY?: MotionValue<number>;
}
/**
* @public
*/
interface LayoutProps {
/**
* If `true`, this component will automatically animate to its new position when
* its layout changes.
*
* ```jsx
* <motion.div layout />
* ```
*
* This will perform a layout animation using performant transforms. Part of this technique
* involved animating an element's scale. This can introduce visual distortions on children,
* `boxShadow` and `borderRadius`.
*
* To correct distortion on immediate children, add `layout` to those too.
*
* `boxShadow` and `borderRadius` will automatically be corrected if they are already being
* animated on this component. Otherwise, set them directly via the `initial` prop.
*
* If `layout` is set to `"position"`, the size of the component will change instantly and
* only its position will animate. If `layout` is set to `"size"`, the position of the
* component will change instantly but its size will animate.
*
* If `layout` is set to `"size"`, the position of the component will change instantly and
* only its size will animate.
*
* If `layout` is set to `"preserve-aspect"`, the component will animate size & position if
* the aspect ratio remains the same between renders, and just position if the ratio changes.
*
* @public
*/
layout?: boolean | "position" | "size" | "preserve-aspect";
/**
* Enable shared layout transitions between different components with the same `layoutId`.
*
* When a component with a layoutId is removed from the React tree, and then
* added elsewhere, it will visually animate from the previous component's bounding box
* and its latest animated values.
*
* ```jsx
* {items.map(item => (
* <motion.li layout>
* {item.name}
* {item.isSelected && <motion.div layoutId="underline" />}
* </motion.li>
* ))}
* ```
*
* If the previous component remains in the tree it will crossfade with the new component.
*
* @public
*/
layoutId?: string;
/**
* A callback that will fire when a layout animation on this component starts.
*
* @public
*/
onLayoutAnimationStart?(): void;
/**
* A callback that will fire when a layout animation on this component completes.
*
* @public
*/
onLayoutAnimationComplete?(): void;
/**
* @public
*/
layoutDependency?: any;
/**
* Wether a projection node should measure its scroll when it or its descendants update their layout.
*
* @public
*/
layoutScroll?: boolean;
}
declare enum AnimationType {
Animate = "animate",
Hover = "whileHover",
Tap = "whileTap",
Drag = "whileDrag",
Focus = "whileFocus",
InView = "whileInView",
Exit = "exit"
}
declare type AnimationDefinition = VariantLabels | TargetAndTransition | TargetResolver;
declare type AnimationOptions$1 = {
delay?: number;
transitionOverride?: Transition;
custom?: any;
type?: AnimationType;
};
declare function animateVisualElement(visualElement: VisualElement, definition: AnimationDefinition, options?: AnimationOptions$1): Promise<void>;
declare type LayoutMeasureListener = (layout: Box, prevLayout?: Box) => void;
declare type BeforeLayoutMeasureListener = () => void;
declare type LayoutUpdateListener = (layout: Axis, prevLayout: Axis) => void;
declare type UpdateListener = (latest: ResolvedValues) => void;
declare type AnimationStartListener = (definition: AnimationDefinition) => void;
declare type AnimationCompleteListener = (definition: AnimationDefinition) => void;
declare type LayoutAnimationStartListener = () => void;
declare type LayoutAnimationCompleteListener = () => void;
declare type SetAxisTargetListener = () => void;
declare type RenderListener = () => void;
interface LayoutLifecycles {
onBeforeLayoutMeasure?(box: Box): void;
onLayoutMeasure?(box: Box, prevBox: Box): void;
}
interface AnimationLifecycles {
/**
* Callback with latest motion values, fired max once per frame.
*
* ```jsx
* function onUpdate(latest) {
* console.log(latest.x, latest.opacity)
* }
*
* <motion.div animate={{ x: 100, opacity: 0 }} onUpdate={onUpdate} />
* ```
*/
onUpdate?(latest: ResolvedValues): void;
/**
* Callback when animation defined in `animate` begins.
*
* The provided callback will be called with the triggering animation definition.
* If this is a variant, it'll be the variant name, and if a target object
* then it'll be the target object.
*
* This way, it's possible to figure out which animation has started.
*
* ```jsx
* function onStart() {
* console.log("Animation started")
* }
*
* <motion.div animate={{ x: 100 }} onAnimationStart={onStart} />
* ```
*/
onAnimationStart?(definition: AnimationDefinition): void;
/**
* Callback when animation defined in `animate` is complete.
*
* The provided callback will be called with the triggering animation definition.
* If this is a variant, it'll be the variant name, and if a target object
* then it'll be the target object.
*
* This way, it's possible to figure out which animation has completed.
*
* ```jsx
* function onComplete() {
* console.log("Animation completed")
* }
*
* <motion.div
* animate={{ x: 100 }}
* onAnimationComplete={definition => {
* console.log('Completed animating', definition)
* }}
* />
* ```
*/
onAnimationComplete?(definition: AnimationDefinition): void;
}
declare type VisualElementLifecycles = LayoutLifecycles & AnimationLifecycles;
interface LifecycleManager {
onLayoutMeasure: (callback: LayoutMeasureListener) => () => void;
notifyLayoutMeasure: LayoutMeasureListener;
onBeforeLayoutMeasure: (callback: BeforeLayoutMeasureListener) => () => void;
notifyBeforeLayoutMeasure: BeforeLayoutMeasureListener;
onLayoutUpdate: (callback: LayoutUpdateListener) => () => void;
notifyLayoutUpdate: LayoutUpdateListener;
onUpdate: (callback: UpdateListener) => () => void;
notifyUpdate: UpdateListener;
onAnimationStart: (callback: AnimationStartListener) => () => void;
notifyAnimationStart: AnimationStartListener;
onAnimationComplete: (callback: AnimationCompleteListener) => () => void;
notifyAnimationComplete: AnimationCompleteListener;
onLayoutAnimationStart: (callback: LayoutAnimationStartListener) => () => void;
notifyLayoutAnimationStart: LayoutAnimationStartListener;
onLayoutAnimationComplete: (callback: LayoutAnimationCompleteListener) => () => void;
notifyLayoutAnimationComplete: LayoutAnimationCompleteListener;
onSetAxisTarget: (callback: SetAxisTargetListener) => () => void;
notifySetAxisTarget: SetAxisTargetListener;
onRender: (callback: RenderListener) => () => void;
notifyRender: RenderListener;
onUnmount: (callback: () => void) => () => void;
notifyUnmount: () => void;
clearAllListeners: () => void;
updatePropListeners: (props: MotionProps) => void;
}
/** @public */
interface EventInfo {
point: Point;
}
/**
* @public
*/
interface FocusHandlers {
/**
* Properties or variant label to animate to while the focus gesture is recognised.
*
* ```jsx
* <motion.input whileFocus={{ scale: 1.2 }} />
* ```
*/
whileFocus?: VariantLabels | TargetAndTransition;
}
/**
* Passed in to tap event handlers like `onTap` the `TapInfo` object contains
* information about the tap gesture such as it‘s location.
*
* ```jsx
* function onTap(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onTap={onTap} />
* ```
*
* @public
*/
interface TapInfo {
/**
* Contains `x` and `y` values for the tap gesture relative to the
* device or page.
*
* ```jsx
* function onTapStart(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onTapStart={onTapStart} />
* ```
*
* @public
*/
point: Point;
}
/**
* @public
*/
interface TapHandlers {
/**
* Callback when the tap gesture successfully ends on this element.
*
* ```jsx
* function onTap(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onTap={onTap} />
* ```
*
* @param event - The originating pointer event.
* @param info - An {@link TapInfo} object containing `x` and `y` values for the `point` relative to the device or page.
*/
onTap?(event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo): void;
/**
* Callback when the tap gesture starts on this element.
*
* ```jsx
* function onTapStart(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onTapStart={onTapStart} />
* ```
*
* @param event - The originating pointer event.
* @param info - An {@link TapInfo} object containing `x` and `y` values for the `point` relative to the device or page.
*/
onTapStart?(event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo): void;
/**
* Callback when the tap gesture ends outside this element.
*
* ```jsx
* function onTapCancel(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onTapCancel={onTapCancel} />
* ```
*
* @param event - The originating pointer event.
* @param info - An {@link TapInfo} object containing `x` and `y` values for the `point` relative to the device or page.
*/
onTapCancel?(event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo): void;
/**
* Properties or variant label to animate to while the component is pressed.
*
* ```jsx
* <motion.div whileTap={{ scale: 0.8 }} />
* ```
*/
whileTap?: VariantLabels | TargetAndTransition;
}
/**
* @public
*/
interface PanHandlers {
/**
* Callback function that fires when the pan gesture is recognised on this element.
*
* **Note:** For pan gestures to work correctly with touch input, the element needs
* touch scrolling to be disabled on either x/y or both axis with the
* [touch-action](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) CSS rule.
*
* ```jsx
* function onPan(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onPan={onPan} />
* ```
*
* @param event - The originating pointer event.
* @param info - A {@link PanInfo} object containing `x` and `y` values for:
*
* - `point`: Relative to the device or page.
* - `delta`: Distance moved since the last event.
* - `offset`: Offset from the original pan event.
* - `velocity`: Current velocity of the pointer.
*/
onPan?(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void;
/**
* Callback function that fires when the pan gesture begins on this element.
*
* ```jsx
* function onPanStart(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onPanStart={onPanStart} />
* ```
*
* @param event - The originating pointer event.
* @param info - A {@link PanInfo} object containing `x`/`y` values for:
*
* - `point`: Relative to the device or page.
* - `delta`: Distance moved since the last event.
* - `offset`: Offset from the original pan event.
* - `velocity`: Current velocity of the pointer.
*/
onPanStart?(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void;
/**
* Callback function that fires when we begin detecting a pan gesture. This
* is analogous to `onMouseStart` or `onTouchStart`.
*
* ```jsx
* function onPanSessionStart(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onPanSessionStart={onPanSessionStart} />
* ```
*
* @param event - The originating pointer event.
* @param info - An {@link EventInfo} object containing `x`/`y` values for:
*
* - `point`: Relative to the device or page.
*/
onPanSessionStart?(event: MouseEvent | TouchEvent | PointerEvent, info: EventInfo): void;
/**
* Callback function that fires when the pan gesture ends on this element.
*
* ```jsx
* function onPanEnd(event, info) {
* console.log(info.point.x, info.point.y)
* }
*
* <motion.div onPanEnd={onPanEnd} />
* ```
*
* @param event - The originating pointer event.
* @param info - A {@link PanInfo} object containing `x`/`y` values for:
*
* - `point`: Relative to the device or page.
* - `delta`: Distance moved since the last event.
* - `offset`: Offset from the original pan event.
* - `velocity`: Current velocity of the pointer.
*/
onPanEnd?(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void;
}
/**
* @public
*/
interface HoverHandlers {
/**
* Properties or variant label to animate to while the hover gesture is recognised.
*
* ```jsx
* <motion.div whileHover={{ scale: 1.2 }} />
* ```
*/
whileHover?: VariantLabels | TargetAndTransition;
/**
* Callback function that fires when pointer starts hovering over the component.
*
* ```jsx
* <motion.div onHoverStart={() => console.log('Hover starts')} />
* ```
*/
onHoverStart?(event: MouseEvent, info: EventInfo): void;
/**
* Callback function that fires when pointer stops hovering over the component.
*
* ```jsx
* <motion.div onHoverEnd={() => console.log("Hover ends")} />
* ```
*/
onHoverEnd?(event: MouseEvent, info: EventInfo): void;
}
declare type ViewportEventHandler = (entry: IntersectionObserverEntry | null) => void;
interface ViewportOptions {
root?: RefObject<Element>;
once?: boolean;
margin?: string;
amount?: "some" | "all" | number;
fallback?: boolean;
}
interface ViewportProps {
whileInView?: VariantLabels | TargetAndTransition;
onViewportEnter?: ViewportEventHandler;
onViewportLeave?: ViewportEventHandler;
viewport?: ViewportOptions;
}
/**
* Either a string, or array of strings, that reference variants defined via the `variants` prop.
* @public
*/
declare type VariantLabels = string | string[];
interface TransformProperties {
x?: string | number;
y?: string | number;
z?: string | number;
translateX?: string | number;
translateY?: string | number;
translateZ?: string | number;
rotate?: string | number;
rotateX?: string | number;
rotateY?: string | number;
rotateZ?: string | number;
scale?: string | number;
scaleX?: string | number;
scaleY?: string | number;
scaleZ?: string | number;
skew?: string | number;
skewX?: string | number;
skewY?: string | number;
originX?: string | number;
originY?: string | number;
originZ?: string | number;
perspective?: string | number;
transformPerspective?: string | number;
}
/**
* @public
*/
interface SVGPathProperties {
pathLength?: number;
pathOffset?: number;
pathSpacing?: number;
}
interface CustomStyles {
/**
* Framer Library custom prop types. These are not actually supported in Motion - preferably
* we'd have a way of external consumers injecting supported styles into this library.
*/
size?: string | number;
radius?: string | number;
shadow?: string;
image?: string;
}
declare type MakeMotion<T> = MakeCustomValueType<{
[K in keyof T]: T[K] | MotionValue<number> | MotionValue<string> | MotionValue<any>;
}>;
declare type MotionCSS = MakeMotion<Omit$1<CSSProperties, "rotate" | "scale" | "perspective">>;
/**
* @public
*/
declare type MotionTransform = MakeMotion<TransformProperties>;
/**
* @public
*/
declare type MotionStyle = MotionCSS & MotionTransform & MakeMotion<SVGPathProperties> & MakeCustomValueType<CustomStyles>;
/**
* @public
*/
interface RelayoutInfo {
delta: {
x: number;
y: number;
width: number;
height: number;
};
}
/**
* @public
*/
declare type ResolveLayoutTransition = (info: RelayoutInfo) => Transition | boolean;
/**
* @public
*/
interface AnimationProps {
/**
* Properties, variant label or array of variant labels to start in.
*
* Set to `false` to initialise with the values in `animate` (disabling the mount animation)
*
* ```jsx
* // As values
* <motion.div initial={{ opacity: 1 }} />
*
* // As variant
* <motion.div initial="visible" variants={variants} />
*
* // Multiple variants
* <motion.div initial={["visible", "active"]} variants={variants} />
*
* // As false (disable mount animation)
* <motion.div initial={false} animate={{ opacity: 0 }} />
* ```
*/
initial?: boolean | Target | VariantLabels;
/**
* Values to animate to, variant label(s), or `AnimationControls`.
*
* ```jsx
* // As values
* <motion.div animate={{ opacity: 1 }} />
*
* // As variant
* <motion.div animate="visible" variants={variants} />
*
* // Multiple variants
* <motion.div animate={["visible", "active"]} variants={variants} />
*
* // AnimationControls
* <motion.div animate={animation} />
* ```
*/
animate?: AnimationControls | TargetAndTransition | VariantLabels | boolean;
/**
* A target to animate to when this component is removed from the tree.
*
* This component **must** be the first animatable child of an `AnimatePresence` to enable this exit animation.
*
* This limitation exists because React doesn't allow components to defer unmounting until after
* an animation is complete. Once this limitation is fixed, the `AnimatePresence` component will be unnecessary.
*
* ```jsx
* import { AnimatePresence, motion } from 'framer-motion'
*
* export const MyComponent = ({ isVisible }) => {
* return (
* <AnimatePresence>
* {isVisible && (
* <motion.div
* initial={{ opacity: 0 }}
* animate={{ opacity: 1 }}
* exit={{ opacity: 0 }}
* />
* )}
* </AnimatePresence>
* )
* }
* ```
*/
exit?: TargetAndTransition | VariantLabels;
/**
* Variants allow you to define animation states and organise them by name. They allow
* you to control animations throughout a component tree by switching a single `animate` prop.
*
* Using `transition` options like `delayChildren` and `staggerChildren`, you can orchestrate
* when children animations play relative to their parent.
*
* After passing variants to one or more `motion` component's `variants` prop, these variants
* can be used in place of values on the `animate`, `initial`, `whileFocus`, `whileTap` and `whileHover` props.
*
* ```jsx
* const variants = {
* active: {
* backgroundColor: "#f00"
* },
* inactive: {
* backgroundColor: "#fff",
* transition: { duration: 2 }
* }
* }
*
* <motion.div variants={variants} animate="active" />
* ```
*/
variants?: Variants;
/**
* Default transition. If no `transition` is defined in `animate`, it will use the transition defined here.
* ```jsx
* const spring = {
* type: "spring",
* damping: 10,
* stiffness: 100
* }
*
* <motion.div transition={spring} animate={{ scale: 1.2 }} />
* ```
*/
transition?: Transition;
}
/**
* @public
*/
interface MotionAdvancedProps {
/**
* Custom data to use to resolve dynamic variants differently for each animating component.
*
* ```jsx
* const variants = {
* visible: (custom) => ({
* opacity: 1,
* transition: { delay: custom * 0.2 }
* })
* }
*
* <motion.div custom={0} animate="visible" variants={variants} />
* <motion.div custom={1} animate="visible" variants={variants} />
* <motion.div custom={2} animate="visible" variants={variants} />
* ```
*
* @public
*/
custom?: any;
/**
* @public
* Set to `false` to prevent inheriting variant changes from its parent.
*/
inherit?: boolean;
}
/**
* Props for `motion` components.
*
* @public
*/
interface MotionProps extends AnimationProps, VisualElementLifecycles, PanHandlers, TapHandlers, HoverHandlers, FocusHandlers, ViewportProps, DraggableProps, LayoutProps, MotionAdvancedProps {
/**
*
* The React DOM `style` prop, enhanced with support for `MotionValue`s and separate `transform` values.
*
* ```jsx
* export const MyComponent = () => {
* const x = useMotionValue(0)
*
* return <motion.div style={{ x, opacity: 1, scale: 0.5 }} />
* }
* ```
*/
style?: MotionStyle;
/**
* By default, Framer Motion generates a `transform` property with a sensible transform order. `transformTemplate`
* can be used to create a different order, or to append/preprend the automatically generated `transform` property.
*
* ```jsx
* <motion.div
* style={{ x: 0, rotate: 180 }}
* transformTemplate={
* ({ x, rotate }) => `rotate(${rotate}deg) translateX(${x}px)`
* }
* />
* ```
*
* @param transform - The latest animated transform props.
* @param generatedTransform - The transform string as automatically generated by Framer Motion
*
* @public
*/
transformTemplate?(transform: TransformProperties, generatedTransform: string): string;
/**
* Internal.
*
* This allows values to be transformed before being animated or set as styles.
*
* For instance, this allows custom values in Framer Library like `size` to be converted into `width` and `height`.
* It also allows us a chance to take a value like `Color` and convert it to an animatable color string.
*
* A few structural typing changes need making before this can be a public property:
* - Allow `Target` values to be appended by user-defined types (delete `CustomStyles` - does `size` throw a type error?)
* - Extract `CustomValueType` as a separate user-defined type (delete `CustomValueType` and animate a `Color` - does this throw a type error?).
*
* @param values -
*/
transformValues?<V extends ResolvedValues>(values: V): V;
}
/**
* @public
*/
declare type ResolvedKeyframesTarget = [null, ...number[]] | number[] | [null, ...string[]] | string[];
/**
* @public
*/
declare type KeyframesTarget = ResolvedKeyframesTarget | [null, ...CustomValueType[]] | CustomValueType[];
/**
* @public
*/
declare type ResolvedSingleTarget = string | number;
/**
* @public
*/
declare type SingleTarget = ResolvedSingleTarget | CustomValueType;
/**
* @public
*/
declare type ResolvedValueTarget = ResolvedSingleTarget | ResolvedKeyframesTarget;
/**
* @public
*/
declare type ValueTarget = SingleTarget | KeyframesTarget;
/**
* A function that accepts a progress value between `0` and `1` and returns a
* new one.
*
* ```jsx
* <motion.div
* animate={{ opacity: 0 }}
* transition={{
* duration: 1,
* ease: progress => progress * progress
* }}
* />
* ```
*
* @public
*/
declare type EasingFunction = (v: number) => number;
/**
* The easing function to use. Set as one of:
*
* - The name of an in-built easing function.
* - An array of four numbers to define a cubic bezier curve.
* - An easing function, that accepts and returns a progress value between `0` and `1`.
*
* @public
*/
declare type Easing = [number, number, number, number] | "linear" | "easeIn" | "easeOut" | "easeInOut" | "circIn" | "circOut" | "circInOut" | "backIn" | "backOut" | "backInOut" | "anticipate" | EasingFunction;
/**
* Options for orchestrating the timing of animations.
*
* @public
*/
interface Orchestration {
/**
* Delay the animation by this duration (in seconds). Defaults to `0`.
*
* @remarks
* ```javascript
* const transition = {
* delay: 0.2
* }
* ```
*
* @public
*/
delay?: number;
/**
* Describes the relationship between the transition and its children. Set
* to `false` by default.
*
* @remarks
* When using variants, the transition can be scheduled in relation to its
* children with either `"beforeChildren"` to finish this transition before
* starting children transitions, `"afterChildren"` to finish children
* transitions before starting this transition.
*
* ```jsx
* const list = {
* hidden: {
* opacity: 0,
* transition: { when: "afterChildren" }
* }
* }
*
* const item = {
* hidden: {
* opacity: 0,
* transition: { duration: 2 }
* }
* }
*
* return (
* <motion.ul variants={list} animate="hidden">
* <motion.li variants={item} />
* <motion.li variants={item} />
* </motion.ul>
* )
* ```
*
* @public
*/
when?: false | "beforeChildren" | "afterChildren" | string;
/**
* When using variants, children animations will start after this duration
* (in seconds). You can add the `transition` property to both the `Frame` and the `variant` directly. Adding it to the `variant` generally offers more flexibility, as it allows you to customize the delay per visual state.
*
* ```jsx
* const container = {
* hidden: { opacity: 0 },
* show: {
* opacity: 1,
* transition: {
* delayChildren: 0.5
* }
* }
* }
*
* const item = {
* hidden: { opacity: 0 },
* show: { opacity: 1 }
* }
*
* return (
* <motion.ul
* variants={container}
* initial="hidden"
* animate="show"
* >
* <motion.li variants={item} />
* <motion.li variants={item} />
* </motion.ul>
* )
* ```
*
* @public
*/
delayChildren?: number;
/**
* When using variants, animations of child components can be staggered by this
* duration (in seconds).
*
* For instance, if `staggerChildren` is `0.01`, the first child will be
* delayed by `0` seconds, the second by `0.01`, the third by `0.02` and so
* on.
*
* The calculated stagger delay will be added to `delayChildren`.
*
* ```jsx
* const container = {
* hidden: { opacity: 0 },
* show: {
* opacity: 1,
* transition: {
* staggerChildren: 0.5
* }
* }
* }
*
* const item = {
* hidden: { opacity: 0 },
* show: { opacity: 1 }
* }
*
* return (
* <motion.ol
* variants={container}
* initial="hidden"
* animate="show"
* >
* <motion.li variants={item} />
* <motion.li variants={item} />
* </motion.ol>
* )
* ```
*
* @public
*/
staggerChildren?: number;
/**
* The direction in which to stagger children.
*
* A value of `1` staggers from the first to the last while `-1`
* staggers from the last to the first.
*
* ```jsx
* const container = {
* hidden: { opacity: 0 },
* show: {
* opacity: 1,
* transition: {
* delayChildren: 0.5,
* staggerDirection: -1
* }
* }
* }
*
* const item = {
* hidden: { opacity: 0 },
* show: { opacity: 1 }
* }
*
* return (
* <motion.ul
* variants={container}
* initial="hidden"
* animate="show"
* >
* <motion.li variants={item} size={50} />
* <motion.li variants={item} size={50} />
* </motion.ul>
* )
* ```
*
* @public
*/
staggerDirection?: number;
}
interface Repeat {
/**
* The number of times to repeat the transition. Set to `Infinity` for perpetual repeating.
*
* Without setting `repeatType`, this will loop the animation.
*
* ```jsx
* <motion.div
* animate={{ rotate: 180 }}
* transition={{ repeat: Infinity, duration: 2 }}
* />
* ```
*
* @public
*/
repeat?: number;
/**
* How to repeat the animation. This can be either:
*
* "loop": Repeats the animation from the start
*
* "reverse": Alternates between forward and backwards playback
*
* "mirror": Switches `from` and `to` alternately
*
* ```jsx
* <motion.div
* animate={{ rotate: 180 }}
* transition={{
* repeat: 1,
* repeatType: "reverse",
* duration: 2
* }}
* />
* ```
*
* @public
*/
repeatType?: "loop" | "reverse" | "mirror";
/**
* When repeating an animation, `repeatDelay` will set the
* duration of the time to wait, in seconds, between each repetition.
*
* ```jsx
* <motion.div
* animate={{ rotate: 180 }}
* transition={{ repeat: Infinity, repeatDelay: 1 }}
* />
* ```
*
* @public
*/
repeatDelay?: number;
}
/**
* An animation that animates between two or more values over a specific duration of time.
* This is the default animation for non-physical values like `color` and `opacity`.
*
* @public
*/
interface Tween extends Repeat {
/**
* Set `type` to `"tween"` to use a duration-based tween animation.
* If any non-orchestration `transition` values are set without a `type` property,
* this is used as the default animation.
*
* ```jsx
* <motion.path
* animate={{ pathLength: 1 }}
* transition={{ duration: 2, type: "tween" }}
* />
* ```
*
* @public
*/
type?: "tween";
/**
* The duration of the tween animation. Set to `0.3` by default, 0r `0.8` if animating a series of keyframes.
*
* ```jsx
* const variants = {
* visible: {
* opacity: 1,
* transition: { duration: 2 }
* }
* }
* ```
*
* @public
*/
duration?: number;
/**
* The easing function to use. Set as one of the below.
*
* - The name of an existing easing function.
*
* - An array of four numbers to define a cubic bezier curve.
*
* - An easing function, that accepts and returns a value `0-1`.
*
* If the animating value is set as an array of multiple values for a keyframes
* animation, `ease` can be set as an array of easing functions to set different easings between
* each of those values.
*
*
* ```jsx
* <motion.div
* animate={{ opacity: 0 }}
* transition={{ ease: [0.17, 0.67, 0.83, 0.67] }}
* />
* ```
*
* @public
*/
ease?: Easing | Easing[];