Categories
React

Framer Motion — MotionValues

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.

Injecting MotionValues

We can inject MotionValues into our components.

They’ll be reflected in all the components.

For example, we can write:

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

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

  return (
    <>
      <motion.div
        drag="x"
        style={{ x, backgroundColor: "red", width: 50, height: 50 }}
      />
      <motion.svg drag="x">
        <motion.circle cx={x} cy="30" r="20" stroke-width="3" fill="red" />
      </motion.svg>
    </>
  );
}

We add the motion.div and motion.svg to set the x position when we drag on both the div and the circle.

We set the x property in the style prop for HTML elements.

For SVGs, we set x as the value of the attribute.

We can watch the latest value of a MotionValue with the onChange method.

For instance, we can write:

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

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

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

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

To call x.onChange in the useEffect callback.

And we get the latest value of x with the latest parameter.

Creating Child MotionValues

We can create MotionValues that are derived from other motion values.

To do this, we can use the useTransform hook:

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

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

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

We create the y MotionValue from the x MotionValue by multiplying it by 2.

And then we pass them both into the style prop.

Now the square will move diagonally when we drag it.

We can also pass in an array of values into the useTransform hook to map the input value to the output value.

For instance, we can write:

import { motion, useMotionValue, useTransform } from "framer-motion";
import React from "react";
const xInput = [-300, 0, 300];
const opacityOutput = [0, 1, 0];
const colorOutput = ["#f00", "#fff", "#0f0"];
export default function App() {
  const x = useMotionValue(0);

const opacity = useTransform(x, xInput, opacityOutput);
  const color = useTransform(x, xInput, colorOutput);

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

We specify the MotionValue to map from in the first argument.

The 2nd argument has the range of the x values.

And the 3rd argument is the values that the values in the 2nd argument map to.

Then we apply the values by passing them into the style prop.

And we see the text color and opacity change in the div.

Conclusion

We can use MotionValues to style our animations with Framer Motion.

Categories
React

Framer Motion — Drag Events and MotionValues

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.

Drag Events

We can listen for drag events with the onDrag , onDragStart , onDragEnd , onDirectionLock events.

For example, we can write:

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

export default function App() {
  return (
    <>
      <motion.div
        style={{ backgroundColor: "red", width: 100, height: 100 }}
        drag="x"
        onDrag={(event, info) => console.log(info.point.x, info.point.y)}
        onDragStart={(event, info) => console.log(info.point.x, info.point.y)}
        onDragEnd={(event, info) => console.log(info.point.x, info.point.y)}
      />
    </>
  );
}

to let us listen to the drag events.

info.point.x and info.point.y have the x and y coordinates of the drag position.

We can also add the onDirectionLock event to listen for drag direction locks.

For example, we can write:

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

export default function App() {
  return (
    <>
      <motion.div
        style={{ backgroundColor: "red", width: 100, height: 100 }}
        drag="x"
        dragDirectionLock
        onDirectionLock={(axis) => console.log(axis)}
        onDrag={(event, info) => console.log(info.point.x, info.point.y)}
        onDragStart={(event, info) => console.log(info.point.x, info.point.y)}
        onDragEnd={(event, info) => console.log(info.point.x, info.point.y)}
      />
    </>
  );
}

We set the dragDirectionLock prop to true and the drag prop to 'x' , so the axis parameter will be set to 'x' .

MotionValue

MotionValues track the state and velocity of animating values.

We can create MotionValues manually to set and get their state, pass multiple components to synchronize motion across components, chain MotionValues with the useTransform hook, and update visual properties without re-rendering.

For example, we can use them to change the opacity of a div when we drag it by writing:

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

const input = [-200, 0, 200];
const output = [0, 1, 0];

export default function App() {
  const x = useMotionValue(0);
  const opacity = useTransform(x, input, output);

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

The useMotionValue hook is used to create the MotionValue.

Then we use the useTransform value to map the x value to the input and output .

The input array has the position range in an array.

The output array has the opacity range.

So the position is changed when we drag, and that returns the opacity of the div.

We can get and set MotionValues.

The set method sets the MotionValue.

And the get and getVelocity methods gets the position and velocity respectively.

For example, we can write:

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

const input = [-200, 0, 200];
const output = [0, 1, 0];

export default function App() {
  const x = useMotionValue(0);
  const opacity = useTransform(x, input, output);

  useEffect(() => {
    x.set(100);
  }, []);

  useEffect(() => {
    console.log(x.get());
    console.log(x.getVelocity());
  }, [x]);

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

to set the position with set and get the position and velocity with get and getVelocity .

Conclusion

We can get and set MotionValue values and handle drag events with Framer Motion.

Categories
React

Framer Motion — Drag Animation

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.

Drag and Layout Animations

We can animate our elements when we drag it.

For example, we can write:

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

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

to add a div and then make it draggable.

We make it draggable by setting the drag prop to 'x' to let us drag horizontally.

The dragConstraints prop lets us set the limits of the dragging.

Also, we can set the drag limit to the bounds of another element.

For instance, we can write:

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

export default function App() {
  const constraintsRef = useRef(null);

  return (
    <motion.div
      ref={constraintsRef}
      style={{ backgroundColor: "green", width: 200, height: 200 }}
    >
      <motion.div
        style={{ backgroundColor: "red", width: 100, height: 100 }}
        drag
        dragConstraints={constraintsRef}
      />
    </motion.div>
  );
}

to set the drag limit to the bound of the outer div.

We can also set the degree of movement allowed outside the constraints.

For example, we can write:

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

export default function App() {
  return (
    <motion.div
      drag
      dragConstraints={{ left: 0, right: 300 }}
      dragElastic={0.2}
      style={{ backgroundColor: "red", width: 100, height: 100 }}
    />
  );
}

We set the dragElastic prop to set the amount of movement allowed outside the drag constraints.

The dragMomentum prop lets us apply momentum from the pan gesture of the component while dragging finishes.

It’s true by default.

We can use it by writing:

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

export default function App() {
  return (
    <motion.div
      drag
      dragConstraints={{ left: 0, right: 300 }}
      dragMomentum={false}
      style={{ backgroundColor: "red", width: 100, height: 100 }}
    />
  );
}

to remove the extra movement after we finish dragging.

The dragPropagation prop lets us allow drag gestures to propagate to child components.

For example, we can write:

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

export default function App() {
  return (
    <motion.div
      drag="x"
      style={{ backgroundColor: "red", width: 100, height: 100 }}
    >
      <motion.div
        drag="x"
        dragConstraints={{ left: 0, right: 300 }}
        dragPropagation
        style={{
          backgroundColor: "green",
          width: 50,
          height: 50
        }}
      ></motion.div>
    </motion.div>
  );
}

Then when we drag the child div, the parent div will also move.

We can control dragging with the useDragControls hook.

For example, 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 to drag</div>
      <motion.div
        style={{ backgroundColor: "red", width: 100, height: 100 }}
        drag="x"
        dragControls={dragControls}
      />
    </>
  );
}

We set the onPointerDown prop to the starDrag function.

startDrag calls the dragControls.start method to move the red div.

snapToCursor set to true means that the div will move to align to where the cursor is.

So when we click on the 'click to drag' text, the div will move toward the cursor.

Conclusion

We can animate when we drag an element with Framer Motion.

Categories
React

Framer Motion — Handling Gestures

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.

Gestures

Framer Motion is capable of recognizing gestures.

For example, we can write:

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

export default function App() {
  return (
    <motion.button
      whileHover={{
        scale: 1.2,
        transition: { duration: 1 }
      }}
      whileTap={{ scale: 0.9 }}
    >
      hello world
    </motion.button>
  );
}

to change the size of the button when we hover or click on it.

scale changes the size. transition.duration sets the duration of the effect.

We can also set the variants prop to set the effects for the button.

For example, we can write:

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

const variants = {
  buttonVariants: { opacity: 1 },
  iconVariants: { opacity: 0.5 }
};

export default function App() {
  return (
    <motion.button whileTap="tap" whileHover="hover" variants={variants}>
      <svg>
        <motion.path variants={variants} />
      </svg>
    </motion.button>
  );
}

We set the whileTap and whileHover to set the handlers for these gestures.

And we set the variants prop to set the effect that we want to see.

Hover

We can add animation when we hover over an element with the whileHover prop.

For instance, we can write:

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

export default function App() {
  return (
    <motion.div
      whileHover={{ scale: 1.2 }}
      onHoverStart={(e) => {
        console.log(e);
      }}
      onHoverEnd={(e) => {
        console.log(e);
      }}
    >
      hello
    </motion.div>
  );
}

Then we can listen to hover start and hover end events with the onHoverStart and onHoverEnd props.

e has the object that we can get information about the hovering.

Tap

We can also handle animation with the tap event with the whileTap prop.

For example, we can write:

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

export default function App() {
  return <motion.div whileTap={{ scale: 0.8 }}>hello</motion.div>;
}

We set the animation effect we want to apply with the whileTap prop.

And we can listen for taps with the onTap prop:

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

function onTap(event, info) {
  console.log(info.point.x, info.point.y);
}

export default function App() {
  return (
    <motion.div onTap={onTap} whileTap={{ scale: 0.8 }}>
      hello
    </motion.div>
  );
}

We get the position of the tap with the onTap function.

info.point.x and info.point.y have the coordinates of the tap.

Pan

We can also listen for panning events with Framer Motion.

For instance, we can write:

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

function onPan(event, info) {
  console.log(info.point.x, info.point.y);
}

export default function App() {
  return <motion.div onPan={onPan}>hello</motion.div>;
}

to listen for pans.

When we drag the div, onPan will be run.

Framer Motion components also take the onPanStart and onPanEnd props to listen for start and end of panning.

For example, we can write:

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

function onPan(event, info) {
  console.log(info.point.x, info.point.y);
}

function onPanStart(event, info) {
  console.log(info.point.x, info.point.y);
}

function onPanEnd(event, info) {
  console.log(info.point.x, info.point.y);
}

export default function App() {
  return (
    <motion.div onPan={onPan} onPanStart={onPanStart} onPanEnd={onPanEnd}>
      hello
    </motion.div>
  );
}

Conclusion

We can listen for various gestures with the Framer Motion library.

Categories
React

Framer Motion — Shared Layout Animations

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.

Shared Layout Animations

We can create shared layout animations with the AnimateSharedLayout component.

For example, we can write:

App.js

import React, { useState } from "react";
import { AnimatePresence, AnimateSharedLayout, motion } from "framer-motion";
import "./styles.css";

function Item({ text }) {
  const [isOpen, setIsOpen] = useState(false);

const toggleOpen = () => setIsOpen(!isOpen);

return (
    <motion.li layout onClick={toggleOpen} initial={{ borderRadius: 10 }}>
      <motion.div layout>{text}</motion.div>
      <AnimatePresence>{isOpen && <Content />}</AnimatePresence>
    </motion.li>
  );
}

function Content() {
  return (
    <motion.div
      layout
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      <div className="row" />
      <div className="row" />
      <div className="row" />
    </motion.div>
  );
}

const items = [
  { text: "foo", id: 1 },
  { text: "bar", id: 2 },
  { text: "baz", id: 3 }
];

export default function App() {
  return (
    <AnimateSharedLayout>
      <motion.ul layout>
        {items.map(({ text, id }) => (
          <Item text={text} key={id} />
        ))}
      </motion.ul>
    </AnimateSharedLayout>
  );
}

styles.css

html,
body {
  min-height: 100vh;
  padding: 0;
  margin: 0;
}

* {
  box-sizing: border-box;
}

body {
  background-repeat: no-repeat;
  display: flex;
  justify-content: center;
  align-items: center;
}

.App {
  font-family: sans-serif;
  text-align: center;
}

ul,
li {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul {
  width: 300px;
  display: flex;
  flex-direction: column;
  background: white;
  padding: 20px;
  border-radius: 25px;
}

li {
  background-color: rgba(214, 214, 214, 0.5);
  border-radius: 10px;
  padding: 20px;
  margin-bottom: 20px;
  overflow: hidden;
  cursor: pointer;
}

li:last-child {
  margin-bottom: 0px;
}

.avatar {
  width: 40px;
  height: 40px;
  background-color: #666;
  border-radius: 20px;
}

.row {
  width: 100%;
  height: 8px;
  background-color: #999;
  border-radius: 10px;
  margin-top: 12px;
}

We have the Item component that lets us toggle the content of the div.

This is done with the AnimatPresence component which shows the Content component’s content if it’s true .

The row class creates the bars that we want to show.

In App , we have the AnimateSharedLayout component wrapped around our ul to let us show the same animation for each li .

The animation is synced across a set of components that don’t share state.

And they let us perform animation between different components with a common layoutId as they’re added or removed.

When a component with the 1ayoutId prop is removed from one part of the tree and it’s added elsewhere, the new component will automatically animate from the old component’s position.

For example, we can write:

App.js

import React, { useState } from "react";
import { AnimateSharedLayout, motion } from "framer-motion";
import "./styles.css";

export default function App() {
  const [selected, setSelected] = useState(colors[0]);

return (
    <AnimateSharedLayout>
      <ul>
        {colors.map((color) => (
          <Item
            key={color}
            color={color}
            isSelected={selected === color}
            onClick={() => setSelected(color)}
          />
        ))}
      </ul>
    </AnimateSharedLayout>
  );
}

function Item({ color, isSelected, onClick }) {
  return (
    <li className="item" onClick={onClick} style={{ backgroundColor: color }}>
      {isSelected && (
        <motion.div
          layoutId="outline"
          className="outline"
          initial={false}
          animate={{ borderColor: color }}
          transition={spring}
        />
      )}
    </li>
  );
}

const colors = ["red", "green", "blue", "yellow"];

const spring = {
  type: "spring",
  stiffness: 500,
  damping: 30
};

styles.css

html,
body {
  min-height: 100vh;
  padding: 0;
  margin: 0;
}

* {
  box-sizing: border-box;
}

body {
  background-repeat: no-repeat;
  display: flex;
  justify-content: center;
  align-items: center;
}

.App {
  font-family: sans-serif;
  text-align: center;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  width: 280px;
  height: 380px;
}

.item {
  width: 100px;
  height: 50px;
  margin: 20px;
  position: relative;
  cursor: pointer;
  flex-shrink: 0;
}

.outline {
  position: absolute;
  top: -20px;
  left: -20px;
  right: -20px;
  bottom: -20px;
  border: 10px solid white;
}

We add a frame around the li that’s clicked on by toggling the isSelected state in the Item component.

In styles.css , we have the outline class to define the outline.

That is toggled when we toggle the li .

In the App component, we select the item that we clicked on with the setSelected function.

Conclusion

We can add shared layouts animation to perform animations synced across a set of components that don’t share states.

And we can also perform animations between different components with a common layoutId as they’re added or removed.