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.

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.

useMotionTemplate

We can use the useMotionTemplate hook to combine multiple motion values into one using a template string literal.

For example, we can write:

import React from "react";
import {
  motion,
  useSpring,
  useMotionValue,
  useMotionTemplate
} from "framer-motion";

export default function App() {
  const shadowX = useSpring(10);
  const shadowY = useMotionValue(10);
  const shadow = useMotionTemplate`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))`;

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

to combine the shadowX and shadowY motion values by using the useMotionTenmplate tag.

We pass in the shadowX and shadowY values to create the drop shadow effect.

And we pass that into the style prop.

useTransform

The useTransform hook lets us create a motion value that transforms the output of another motion value with a function.

For example, we can write:

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

export default function App() {
  const x = useMotionValue(10);
  const y = useTransform(x, (value) => value * 2);

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

We create the x motion value with the useMotionValue hook.

Then we call the useTransform hook with the x motion value and a function to return a value for the y motion value.

Then we pass them both into the style prop to position the div.

We can use it to return a range of values.

To do this, we write:

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

export default function App() {
  const x = useMotionValue(0);
  const xRange = [-200, -100, 100, 200];
  const opacityRange = [0, 1, 1, 0];
  const opacity = useTransform(x, xRange, opacityRange);

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

We create the xRange array to use it to map to the values of opacityRange .

If x is -200, then opacity is 0. If x is -100, then opacity is 1, and so on.

We then use that to render the div with the position given by the x and y values.

The opacity is set to the value of the div.

useSpring

The useSpring hook lets us create a motion value that when it’s set, will use a spring animation to animate to its new state.

For example, we can write:

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

export default function App() {
  const x = useSpring(0, { stiffness: 300 });
  const y = useSpring(x, { damping: 10 });

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

We call useSpring to create the x motion value to create the spring effect.

stiffness is the stiffness coefficient. Higher stiffness decreases the number of oscillations.

damping is the ratio to reduce the magnitude of the oscillation.

We use the x and y values to animate the position of the div.

Conclusion

We can use various hooks provided by Framer Motion to animate our elements.