Categories
React

Framer Motion — Animation Styles

With the Framer Motion library, we can render animations in our React app easily.

In this article, we’ll take a look at how to get started with Framer Motion.

CSS Variables

Motion components can accept CSS variables

For example, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.ul
        initial={{ "--rotate": "0deg" }}
        animate={{ "--rotate": "360deg" }}
        transition={{ duration: 2, repeat: Infinity }}
      >
        <li style={{ transform: "rotate(var(--rotate))" }}>foo</li>
        <li style={{ transform: "rotate(var(--rotate))" }}>bar</li>
        <li style={{ transform: "rotate(var(--rotate))" }}>baz</li>
      </motion.ul>
    </>
  );
}

We set the rotate CSS variable to a value.

We set the value for it before the animation with the initial prop.

And we set the value for it after the animation with the animate prop.

Then we use them in the li to rotate the li elements.

The transition prop has duration to 2 to rotate for 2 seconds.

And we have repeat set to Infinity to repeat infinite times.

Performance

Framer Motion animates values outside the React render cycle to increase performance.

For example, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.div
        style={{ x: 0, backgroundColor: "red", width: 100, height: 100 }}
        animate={{ x: 100 }}
      />
    </>
  );
}

to get GPU accelerated animation by using the motion.div with motion value x .

If we specify CSS properties in the style and animate props, then we get CPU acceleration.

So if we have:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.div
        style={{
          position: "absolute",
          left: 0,
          backgroundColor: "red",
          width: 100,
          height: 100
        }}
        animate={{ left: 100 }}
      />
    </>
  );
}

Then the left property will be animated by the CPU, which is slower than animating the x motion value with the GPU.

motion components are fully compatible with server-side rendering, except for the scale , rotate , pathLength , pathOffset , and pathSpacing properties.

Props

Motion components haves a few props.

initial

The initial prop lets us set the styles that are applied to the motion component when the animation starts.

For example, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.div
        initial={{ opacity: 1 }}
        style={{
          backgroundColor: "red",
          width: 100,
          height: 100
        }}
        animate={{ opacity: 0 }}
      />
    </>
  );
}

to set the opacity of the div to 1 when we start animating the div.

We can also pass in a variant name as the value of initial :

import { motion } from "framer-motion";
import React from "react";

const variants = {
  visible: {
    opacity: 1,
    transition: { duration: 2 }
  }
};

export default function App() {
  return (
    <>
      <motion.div
        initial="visible"
        variants={variants}
        style={{
          backgroundColor: "red",
          width: 100,
          height: 100
        }}
        animate={{ opacity: 0 }}
      />
    </>
  );
}

We pass in the property name in the variants object to apply the styles within the property.

Conclusion

We can add animations with better performance with GPU acceleration.

Also, we can set styles to apply when the animation starts.

Categories
React

Framer Motion —Motion Components

With the Framer Motion library, we can render animations in our React app easily.

In this article, we’ll take a look at how to get started with Framer Motion.

Motion Components

Motion components are DOM elements optimized for 60fps animations and gestures.

For example, to create an animated div, we can use the motion.div component.

To do this, we write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.div
        animate={{ rotate: 360 }}
        transition={{ duration: 2 }}
        style={{ backgroundColor: "red", width: 100, height: 100 }}
      />
    </>
  );
}

to add a div that animates by rotating 360 degrees.

And we set the transition prop to set the duration of the animation to 2 seconds.

Supported Values

We can add various types of values for our animation.

We can add:

  • numbers
  • strings
  • colors in hex, RGB, or HSL format
  • complex strings with multiple numbers or colors

Non-animatable values will be set instantly.

If we set these values within transitionEnd , then this value will be set at the end of the animation.

For instance, we can use it by writing;

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.div
        animate={{
          x: 0,
          backgroundColor: "#000",
          boxShadow: "10px 10px 0 rgba(0, 0, 0, 0.2)",
          position: "fixed",
          transitionEnd: {
            display: "none"
          }
        }}
        style={{ backgroundColor: "red", width: 100, height: 100 }}
      />
    </>
  );
}

We set the animate prop to an object with various values.

x is the x coordinate of the element’s position.

backgroundColor has a background color.

boxShadow has the box shadow.

position is set to 'fixed' .

transitionEnd has the value display set to 'none' .

We have all these values applied duration animation.

Value Type Conversion

Values can only be animated between 2 of the same type.

However, x , y , width , height , top , left , right , and bottom values can be animated between 2 different value types.

For instance, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.div
        initial={{ x: "100%" }}
        animate={{ x: "calc(100vw - 50%)" }}
        style={{ backgroundColor: "red", width: 100, height: 100 }}
      />
    </>
  );
}

We have the initial prop set to an object with x set to '100%' .

And in the animare prop, we set x to 'calc(100vw — 50%) .

They’re different types of values, but Framer Motion can convert between them.

Transform

Transform properties are accelerated by the GPU so they can animate smoothly.

Transform properties include:

  • Translate shortcuts: x, y, z
  • Translate: translateX, translateY, translateZ
  • Scale: scale, scaleX, scaleY
  • Rotate: rotate, rotateX, rotateY, rotateZ
  • Skew: skew, skewX, skewY
  • Perspective: transformPerspective

For example, we can write:

import { motion } from "framer-motion";
import React from "react";

export default function App() {
  return (
    <>
      <motion.div
        whileHover={{ scale: 1.2 }}
        whileTap={{ scale: 0.8 }}
        style={{ backgroundColor: "red", width: 100, height: 100 }}
      />
    </>
  );
}

Then when we tap or hover on the div, we’ll see smooth size changes.

Conclusion

Motion components are elements that we can animate with Framer Motion.

Categories
React

Framer Motion — Animation Hooks

With the Framer Motion library, we can render animations in our React app easily.

In this article, we’ll take a look at how to get started with Framer Motion.

useReducedMotion

We can use the useReducedMotion hook to animate our elements based on the current device’s reduced motion setting.

For example, we can write:

import React, { useState } from "react";
import { motion, useReducedMotion } from "framer-motion";

export default function App() {
  const shouldReduceMotion = useReducedMotion();
  const closedX = shouldReduceMotion ? 0 : "-100%";
  const [isOpen, setIsOpen] = useState(true);

  return (
    <>
      <button onClick={() => setIsOpen(!isOpen)}>toggle</button>
      <motion.div
        animate={{
          opacity: isOpen ? 1 : 0,
          x: isOpen ? 0 : closedX
        }}
        style={{ backgroundColor: "red", width: 100, height: 100 }}
      />
    </>
  );
}

We call the useReduceMotion hook to get the shouldReduceMotion variable.

We can use that to check if reduced motion setting is enabled.

Then we can use that to position the div based on the shouldReduceMotion value.

usePresence

The usePresence hook lets us access information about whether an element is still present in the React tree.

If isPresent is false , then a component has been remove from the tree, but AnimatePresence won’t remove it until safeToRemove has been called.

For example, we can write:

import React, { useEffect, useState } from "react";
import { usePresence } from "framer-motion";

export const Component = () => {
  const [isPresent, safeToRemove] = usePresence();

  useEffect(() => {
    console.log(isPresent);
    !isPresent && setTimeout(safeToRemove, 1000);
  }, [isPresent]);

  return <div style={{ backgroundColor: "red", width: 100, height: 100 }} />;
};

export default function App() {
  const [isOpen, setIsOpen] = useState(true);

  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>toggle</button>
      {isOpen && <Component />}
    </div>
  );
}

to watch the isPresent value in the useEffect callback.

Then we call safeToRemove to remove the component when it’s unloaded.

useIsPresent

The useIsPresent hook is similar to usePresence except that it doesn’t return the safeToRemove function.

For example, we can write:

import React, { useEffect, useState } from "react";
import { useIsPresent } from "framer-motion";

export const Component = () => {
  const isPresent = useIsPresent();

  useEffect(() => {
    isPresent && console.log("is present");
  }, [isPresent]);

  return <div style={{ backgroundColor: "red", width: 100, height: 100 }} />;
};

export default function App() {
  const [isOpen, setIsOpen] = useState(true);

  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>toggle</button>
      {isOpen && <Component />}
    </div>
  );
}

to log the isPresent value.

useDragControls

We can use the useDragControls hook to create drag controls.

The drag controls can be passed into the draggable component’s dragControls prop.

It exposes a start method that can start dragging from pointer events or other components.

For instance, we can write:

import { motion, useDragControls } from "framer-motion";
import React from "react";

export default function App() {
  const dragControls = useDragControls();

  function startDrag(event) {
    dragControls.start(event, { snapToCursor: true });
  }

  return (
    <>
      <div onPointerDown={startDrag}>click me</div>
      <motion.div
        style={{ backgroundColor: "red", width: 100, height: 100 }}
        drag="x"
        dragControls={dragControls}
      />
    </>
  );
}

We create the dragControls with the useDragControls hook.

Then we pass that into the dragControls prop.

The drag prop is set to 'x' so that we can drag horizontally.

We also created the startDrag function to call dragControls.start to start dragging.

We pass in the mouse event object into start and set snapToCursor to true to move the div towards our mouse cursor when we click on ‘click me’.

Conclusion

We can use various hooks that come with Framer Motion to control our animation.

Categories
React

Framer Motion — Animate and Map Motion Values

With the Framer Motion library, we can render animations in our React app easily.

In this article, we’ll take a look at how to get started with Framer Motion.

animate

We can use the animate function to animate a single value or a motion value.

For example, we can write:

import React, { useEffect } from "react";
import { animate, motion, useMotionValue } from "framer-motion";

export default function App() {
  const x = useMotionValue(0);

  useEffect(() => {
    const controls = animate(x, 100, {
      type: "spring",
      stiffness: 2000,
      onComplete: (v) => {}
    });

    return controls.stop;
  });

  return (
    <div className="App">
      <motion.div
        style={{ x, backgroundColor: "red", width: 100, height: 100 }}
      />
    </div>
  );
}

to create the x motion value with useMotionValue .

Then we can animate it with the animate function.

We set the type of the animation and the stiffness of it.

Then we return the control.stop method in the callback to stop using it.

Then finally we put the x value into the style prop so we can use it.

Transform

The transform function transforms a number into other values by paying them into an output range.

For example, we can write:

import React, { useEffect, useState } from "react";
import { motion, transform, useMotionValue } from "framer-motion";

export default function App() {
  const x = useMotionValue(0);
  const inputRange = [0, 100];
  const outputRange = [1, 0.3];
  const [opacity, setOpacity] = useState();

  useEffect(() => {
    x.onChange((latest) => {
      const opacity = transform(latest, inputRange, outputRange);
      setOpacity(opacity);
    });
  }, []);

  return (
    <div className="App">
      <motion.div
        drag="x"
        style={{ x, opacity, backgroundColor: "red", width: 100, height: 100 }}
      />
    </div>
  );
}

to call transform to map the x motion value into an opacity value.

We watch the x value with onChange .

Then in the callback, we call transform to map the inputRange , which has the x value range, to the outputRange , which has the opacity values.

It returns the opacity value that we use in the motion.div .

Therefore, we see that the div changes in capacity when we drag it.

Also, we can use the transform function with the inputRange and outputRange array.

Then we can call the convertRange function with the latest x value to create our opacity value:

import React, { useEffect, useState } from "react";
import { motion, transform, useMotionValue } from "framer-motion";

export default function App() {
  const x = useMotionValue(0);
  const inputRange = [0, 100];
  const outputRange = [1, 0.3];
  const [opacity, setOpacity] = useState();

  useEffect(() => {
    x.onChange((latest) => {
      const convertRange = transform(inputRange, outputRange);
      const opacity = convertRange(x.get());
      setOpacity(opacity);
    });
  }, []);

  return (
    <div className="App">
      <motion.div
        drag="x"
        style={{ x, opacity, backgroundColor: "red", width: 100, height: 100 }}
      />
    </div>
  );
}

We did all of that in the onChange callback to create the opacity value and pass that into the style prop.

Conclusion

We can use the animate function to animate motion values.

And we can map motion values to other values with Framer Motion.

Categories
React

Framer Motion — Scrolling and Watching Motion Values

With the Framer Motion library, we can render animations in our React app easily.

In this article, we’ll take a look at how to get started with Framer Motion.

useElementScroll

The useElementScroll hook returns motion values that updated when the element scrolls.

For example, we can write:

import React, { useRef } from "react";
import { motion, useElementScroll } from "framer-motion";

export default function App() {
  const ref = useRef();
  const { scrollYProgress } = useElementScroll(ref);

  return (
    <div ref={ref} style={{ overflow: "scroll", height: 200 }}>
      <motion.div style={{ scaleY: scrollYProgress }}>
        {Array(100)
          .fill()
          .map((_, i) => (
            <p key={i}>{i}</p>
          ))}
      </motion.div>
    </div>
  );
}

We set the scaleY value to the scrollYProgress motion value.

Then the p elements that are lower are taller.

We just have the overflow property set to 'scroll' and a fixed height for this hook to calculate the scroll progress.

useViewportScroll

The useViewportScroll hook returns the motion values that update when the viewport scrolls.

For example, we can write:

import React, { useEffect } from "react";
import { motion, useViewportScroll } from "framer-motion";

export default function App() {
  const { scrollYProgress } = useViewportScroll();

  useEffect(() => {
    scrollYProgress.onChange((latest) => {
      console.log(latest);
    });
  }, []);

  return (
    <motion.div>
      {Array(100)
        .fill()
        .map((_, i) => (
          <p key={i}>{i}</p>
        ))}
    </motion.div>
  );
}

to get the scrollYProgress , which has the latest value of the vertical scroll progress.

Its value is between 0 and 1.

The value is set as the value of the latest parameter.

Other value this hook returns includes scrollX , which is the horizontal scroll distance in pixels.

The scrollY motion value has the vertical scroll distance in pixels.

And scrollXProgress has the horizontal scroll progress between 0 and 1.

onChange

We can watch for motion value changes with the onChange method.

For example, we can call it by writing:

import { motion, transform, useMotionValue } from "framer-motion";
import React, { useEffect } from "react";

export default function App() {
  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.1]);
      opacity.set(newOpacity);
    }

    const unsubscribeX = x.onChange(updateOpacity);
    const unsubscribeY = y.onChange(updateOpacity);

    return () => {
      unsubscribeX();
      unsubscribeY();
    };
  }, []);

  return (
    <motion.div
      drag
      style={{ x, opacity, backgroundColor: "red", width: 100, height: 100 }}
    />
  );
}

We call onChange in the useEffect callback.

We call x.onChange to watch the latest value of x .

And we call y.onChange to watch the latest value of y .

It returns a function to let us unsubscribe to the changes.

The updateOpacity function gets the latest value of x and y and then compute the opacity from it.

We call opacity.set to set the value of the opacity motion value.

Then we render the values by passing it into the style prop.

destroy

The destroy method destroys and clean up subscribers.

We call this on a motion value object.

Conclusion

We can watch for motion value changes to create animations with Framer Motion.

Also, we can watch for scrolling progress with it.