Framer Motion Tutorials: Make More Advanced Animations

Framer Motion is a new and popular open-source React library. It’s aimed at creating production-ready animation.

Framer Motion is Pose’s animation library next-in-line. It possesses a low-level declarative API and can be used irrespective of platform, for the web as well as for mobile apps. Its other advantage valued by the developers is that it’s also possible to get it as a separate package for use in React apps.

Motion is a flexible tool that is great both for beautifully smooth and simple animations and for more advanced sequences. All with as little amount of code as possible.

Framer’s documentation provides enough tutorials on how to do the simplest gestures and motion. However, if you are working with more sophisticated cases, there’s too little information on the web on this respect. So it makes no sense to delve into the simplest examples, they can be done according to the documentation. There are also articles on this topic (albeit not very many of them) on the web. Let’s tinker with more complex things instead.

If you’ve ever wondered how to make this or that butter-smooth effect like, for example, on the Dribbble Global Design Survey 2019 page, then read on!

When to use Framer Motion and why

Framer Motion is capable of powering animations in Framer X, a prototyping tool, which makes the hand-off extremely convenient. The majority of designers have suffered a situation when they spend ages perfecting every little detail of design only to have it lost in the development process. Framer Motion lets you use the same animation library both in prototyping and production. This way you don’t need to worry your animations are different from what you’ve intended them to be.

As for the best way to use animation as an instrument in general, the main thing is to keep it meaningful and relevant to the subject. You can grasp the main idea and a dozen useful tips in this article.

Framer Motion tutorials

Framer Motion is great for animations. Let’s try doing some of those! If at the moment you’re at the beginning of your JavaScript journey, then you’d be better off with simpler things first.

Each tutorial consists of 1-3 components with a list of props (most of which are optional and/or have default values). All examples are interactive, so refresh and click, drag, flip.

Parallax Box

ParallaxBox component animation is set in motion by scrolling, imitating the parallax effect. Scroll the triggers component to shift up/down (depending on the scroll direction) by the value specified in the yOffset prop (px, > 0, by default = 100).

MotionValues are used to track the state and speed of an animating value. 

Usually automatically calculated MotionValues is more than enough for most cases. But for more advanced ones, you can create them manually, and then inject them into components. We’ll do just that.

To animate the ParallaxBox component, use the chain of MotionValues, that are passed to the ParallaxBox via the useTransform hook (useTransform (parent, from, to, options)).

const y = useTransform(
  scrollY,
  yRange, 
  [0, -yOffset], 
  easing
);

useTransform creates a MotionValue that transforms the output of another MotionValue by mapping it from one range of values into another.

The first parameter, parent: the MotionValue to transform the output of.

We’ll use another hook as it’s value:

const { scrollY } = useViewportScroll();

useViewportScroll(): ScrollMotionValues – provides MotionValues that update when the viewport scrolls. scrollY – vertical scroll distance in pixels.

Input values – from: number[] – a linear series of numbers (all either increasing or decreasing).

const yRange = [transformInitialValue, transformFinalValue];

yRange accepts an array consisting of transformInitialValue – the initial position of the element and transformFinalValue – its position at the end of the animation.

Output values – to: T [] – a series of numbers, colors or strings. Must be the same length as inputRange.

In the example, the output values take an array:

[0, -yOffset]

where 0 – initial position, –yOffset – element offset (the value is negative since the component is shifted upward relative to its initial position).

The last value that useTransform takes – options – can take several values, but in this example, the most significant for us is ease: EasingFunction []

easing = [0.42, 0, 0.58, 1],

Easing functions are algorithms that let you control the animation speed to give them the desired effect like bouncing, deceleration, etc.

That is, in real life things don’t start and stop moving abruptly and in a linear fashion. Momentum and other physical aspects play their part. For example, when you play ball you keep in mind that it doesn’t move at a constant speed but bounces. A car decelerates while turning, etc. Easing functions help make effects look more natural and life-like. 

Easing can take either an array of four numbers [n, n, n, n] known as cubic bezier function or build-in named functions like “linear”, “easeInOut”, etc.

Now all that remains is to pass the y value into the component:

return (
   <MotionBox ref={ref} initial={{ y: 0 }} style={{ y, opacity }}>
     {children}
   </MotionBox>
 );

Job’s done! Now the component will smoothly shift up while scrolling, imitating the parallax effect.

You can also pass props into the component:

  • yOffset – offset value
  • easing – animation type
  • triggerPoint – a value between 0 and 1, which determines when the animation of this element is to begin, depending on its position on the page, where 0 is the top of the page and 1 is its bottom.
  • fadeOut is a boolean value that determines whether the element’s fading out will affect its opacity level.

All listed props are optional and already have default values ​​specified in the component.

Easy, isn’t it?

Intersection Observer | Scale Box

 

This tutorial is much simpler than the previous one. The only difficulty is using a hook from a third-party library react-useuseIntersectionReact sensor hook that tracks the changes in the intersection of a target element.

With the help of this hook, we can create the IntersectionObserver component that senses when the motion component appears in its scope and starts the animation.

This component can take a boolean prop reset, with default = false, which is responsible for whether the animation will be triggered when the element appears in the viewport again.

Now we can wrap one or more motion components with the IntersectionObserver component:

<IntersectionObserver>
  <ScaleBox />
</IntersectionObserver>

And pass the inView value to the motion component from the context of the IntersectionObserver component:

const { inView } = useContext(IntersectionContext);

Let’s move on to the motion component – ScaleBox.

A scale value allows you to reduce or increase the size of an element. That is, when the ScaleBox falls into the visible part of the browser window, IntersectionObserver changes the value from the inView context to true, which will trigger the animation:

animate={inView ? "show" : "hidden"}

ScaleBox can be used outside the IntersectionObserver component, but then the animation will start after the page loads and will be processed regardless of whether it is in the visible area or not. Often, this is not the way we would like a motion component to act, this is why we need the IntersectionObserver.

To describe the ScaleBox animation we use Variants – sets of pre-defined target objects:

const variants = {
   hidden: {
     scale: 0,
     opacity: 0,
     transition
   },
   show: {
     scale: 1,
     opacity: 1,
     transition
   }
 };

The Variants object contains key-value pairs, where the keys (labels) are names for the animation properties (in our case, it is “hidden” and “show”, although the names can be anything. The main thing is to keep them meaningful).

The only thing that remains is to pass the variants object to variants prop:

return (
   <MotionBox
     initial="hidden"
     animate={inView ? "show" : "hidden"}
     exit="hidden"
     variants={variants}
   >
     {children}
   </MotionBox>
 );

Variants can set an animation target that are indicated by its labels (for example, initial=”hidden”).

It is also worth considering in more detail the transition property, with which you can set animation execution parameters like duration (s),  delay (s), ease.

const transition = {
  duration: 0.4,
  delay: 0.2,
  ease: "easeInOut"
 };

There are other parameters that can be passed to transition, for example, loop, the number of iterations of the animation (accepts a number or Infinity).

As a result, we got two components. The first – Intersection Observer – detects the presence of the motion element on the screen. The second one – ScaleBox – changes the element’s size. Both components are quite simple. Using them together allows animating the content appearing on the page when it is displayed in the user’s viewport.

Fade-in-up Box | Stagger

FadeInOutBox – a component that animates the appearance of an element, its shift from bottom to top (yOffset) and its opacity.

Like ScaleBox, this component uses variants:

 const variants = {
   hidden: { 
y: yOffset, 
opacity: 0, 
transition 
   },
   show: {
     y: 0,
     opacity: 1,
     transition   
   }
 };

Only in this case, the y property (transforming an element’s position along the y-axis) is animated, not the scale.

The second component of this example is StaggerWrap animating the nested motion components sequentially with a certain delay (staggerChildren).

const variants = {
   hidden: { opacity: 0 },
   show: {
     opacity: 1,
     transition: {
       when: "beforeChildren",
       staggerChildren: 0.5
     }
   }
 };

When prop helps to detail the association between parent and its children (false by default). It’s also can take “beforeChildren” value if parent’s animation has to execute before children’s or “afterChildren” for the opposite case.    

In this case, the animation parameters are transferred to the parent, that is, to the StaggerWrap component itself:

 return (
   <StaggerContext.Provider value={{ stagger: true }}>
     <MotionBox
       initial="hidden"
       animate="show"
       exit="hidden"
       variants={variants}
     >
       {children}
     </MotionBox>
   </StaggerContext.Provider>
 );
};

The only thing left is to pass the variants object into the props options of the children elements:

return stagger ? (
   <MotionBox variants={variants}>
     {children}
   </MotionBox>
 ) : [...]

Using StaggerWrap together with FadeInUpBox allows you to sequentially animate the appearance of multiple elements with a shift on y (yOffset) and an opacity change.

Drag Slider

This component uses the useMotionValue hook to change the value of x (that is, for the slider flipping) to a drag gesture (drag = ”x”).

Using the IntersectionObserver, FadeInOutBox, and ScaleBox components we already know, you can pass “scale” | “fadeIn” values to slideApperance props to add animation to the appearance of slides.

This slider is far from perfect:  overflow-x: hidden on slider wrapper makes it impossible to implement scroll on mouse wheel motion.

Also, it often gets reset on the last slide to its original position for some yet unknown reason.

Motion Slider

Another slider that works not only on drag but also on controls (arrows and bullets).

In order to animate the state of the slide on unmount, we need to use AnimationPresence component and pass exitBeforeEnter prop to correctly finish its exit animation before the next component render.

It’s not a perfect example. In particular, bullets animation only works in one direction and uses the third-party library.


Progress Circle and Progress Bar

These are just fooling around with some animations on the Dribbble’s Global Design Survey page and trying to make something similar.

Some components for displaying statistical information. Best work in combination with IntersectionObserver.

Fade In Up Box | Scale Box

Another take on Dribbble.

There are already familiar StaggerWrap components in this example, FadeInUpBox for animating text, and ScaleBox for animating images. In general, the animation is quite simple, but it looks impressive and interesting.

To wrap up

In general, Framer Motion is a multifunctional, flexible, and modern library.

Its main issue is that a significant part of its functionality is either poorly and fragmentarily described in the documentation, or not described at all. Because of that, you might spend time learning things instead of implementing them.

Happy if this helps you to figure out how to build your own motion components with stunning animations and make your job at least a bit easier.

Written by Julia Shikanova and Kate Shokurova

Mobile app development
Making apps stay on peoples' phones.
Written by Kate Shokurova
December 12, 2019

YOU MAY ALSO LIKE

This is the old design of Shakuro.com

We're currently working on rebranding our company and redesigning the website