Skip to content

alloc/preact-in-motion

Repository files navigation

preact-in-motion

NPM version License Bundle size

This package uses the Preact Options API to introduce an animate prop to every native element (e.g. <div>, <span>, <button>, etc.). It uses the motion/mini package to animate the elements. To understand which features of Motion are supported, see this comparison table.

Open in StackBlitz

Installation

Choose your package manager, then install this package and the motion package.

  • PNPM

    pnpm add preact-in-motion motion
    
  • Bun

    bun add preact-in-motion motion
    
  • Yarn

    yarn add preact-in-motion motion
    
  • NPM

    npm install preact-in-motion motion
    

Usage

Always import the package, so it can install itself into Preact at runtime.

import 'preact-in-motion'

Now you can define the animate prop on any host element (e.g. <div>, <span>, <button>, including SVG elements).

// Animate the opacity when a boolean changes on rerender.
<div animate={{
  opacity: visible ? 1 : 0,
}}>

When the element's parent component is re-rendered, the keyframes will be diffed. If any keyframes are different, a new animation will be scheduled.

Animation options (e.g. duration, ease, etc) may be defined next to the keyframes. See the Motion docs for details.

import { easeInOut } from 'motion'

<div animate={{
  opacity: visible ? 1 : 0,
  duration: 1,
  ease: easeInOut,
}}>

Property-specific options

You may set a transition function to customize the animation options for each style property.

<div animate={{
  opacity: visible ? 1 : 0,
  transform: `scale(${visible ? 1 : 0.5})`,
  transition: prop => ({
    duration: prop === 'opacity' ? 1 : 0.2,
  }),
}}>

Alternatively, the transition prop can be set to an object, with property-specific options.

// Identical to the previous example.
<div animate={{
  opacity: visible ? 1 : 0,
  transform: `scale(${visible ? 1 : 0.5})`,
  transition: {
    duration: 0.2,
    opacity: { duration: 1 },
  },
}}>

Lifecycle animations

The following event-driven animations are supported:

  • initial Style values to be applied before mounting.
  • update Animate when this element is re-rendered.
  • enter Animate when this element is added to the DOM.
  • leave Animate before this element is removed from the DOM. (must use <AnimatePresence>)
  • whileHover Animate when this element is hovered on.
  • whileFocus Animate when this element is focused.
  • whilePress Animate when this element is pressed on.
<div animate={{
  // Start out invisible.
  initial: {
    opacity: 0,
  },
  // Fade in when added to the DOM.
  enter: {
    opacity: 1,
  },
  // Scale up while hovered over.
  whileHover: {
    transform: 'scale(1.1)',
    duration: 0.2,
  },
}}>

AnimatePresence

To animate an element before unmounting it, you must wrap the element or its parent component with an <AnimatePresence> element.

<AnimatePresence>
  {visible && (
    <h1
      animate={{
        // Fade out before unmounting.
        leave: {
          opacity: 0,
        },
      }}>
      Fade Into Obscurity
    </h1>
  )}
</AnimatePresence>

If the children of AnimatePresence could possibly be reordered or toggled (i.e. isHappy in the example below), you must set a key prop on each element. Note that conditionally mounting a child with {visible && (...)} does not require a key prop to be set (in this case, the child's index is used as the default key).

Additionally, you may use the enterDelay prop on AnimatePresence to force “enter animations” to wait. This delay is only applied when a “leave animation” is in progress, making it useful for smooth transitions between elements.

<AnimatePresence enterDelay={0.3}>
  {isHappy ? (
    <span
      key="happy"
      animate={{
        initial: { transform: 'translateX(50px)', opacity: 0 },
        enter: { transform: 'translateX(0)', opacity: 1 },
        leave: { reverse: true },
      }}>
      😄 I'm happy!
    </span>
  ) : (
    <span
      key="sad"
      animate={{
        initial: { transform: 'translateX(-50px)', opacity: 0 },
        enter: { transform: 'translateX(0)', opacity: 1 },
        leave: { reverse: true },
      }}>
      😢 I'm sad...
    </span>
  )}
</AnimatePresence>

You may set reverse: true on the leave prop to copy keyframes from the initial prop.

Easing

Easing functions are provided by the motion package.

  • Spring animations (docs)

    import { spring } from 'motion'
    
    <div animate={{
      transform: 'scale(1.1)',
      type: spring,
      bounce: 1,
      duration: 3,
    }}>
  • Easing functions (docs)

    The ease prop accepts a function, pre-defined string, or a cubic bezier array (e.g. [0.25, 0.1, 0.25, 1]).

    import { easeInOut } from 'motion'
    
    <div animate={{
      transform: 'scale(1.1)',
      ease: easeInOut,
    }}>

    These easing functions can be imported from motion:

    • cubicBezier
    • easeIn / easeOut / easeInOut
    • backIn / backOut / backInOut
    • circIn / circOut / circInOut
    • anticipate
    • steps

License

MIT

About

Light, elegant animation plugin for Preact (powered by Motion.dev and WAAPI)

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published