Framer Motion

Interactive graph

In this lesson we’ll create an interactive graph to solidify our understanding of the hooks we just learned, but also to learn an additional hook. As always, it’s a real-world example, you can see a very similar component on Linear’s features page. This is how the end result will look like:

Framer Motion Interactive graph

Overview

In this lesson we’ll create an interactive graph to solidify our understanding of the hooks we just learned, but also to learn an additional hook. As always, it’s a real-world example, you can see a very similar component on Linear’s features page. This is how the end result will look like:

Making it interactive

Before we add any motion, we first need to make the graph follow the cursor. The end result should look like this:

Our starting point will be a simple static svg graph. This exercise will cover what we just learned in the last lesson, but also a few techniques we have learned earlier in the course, and even a new Framer Motion hook. You can use the hints below if you get stuck. And remember, it’s okay to struggle, that’s how you learn. Good luck!

"use client";

export default function Graph() {
return (
  <div className="wrapper">
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 644 188">
      <path
        stroke="#0090FF"
        strokeWidth="2"
        d="M1 118.5s82.308-15.501 113.735-29 74.769-1.713 121.217-12c37.596-8.328 58.517-15.006 93.781-30.5 80.146-35.215 123.213-16 154.141-24.5S635.97.849 644 1.5"
      ></path>
      <path
        fill="url(#paint0_linear_540_31)"
        d="M113.912 89.012C82.437 102.511 1 118.01 1 118.01V188h643V1.023c-8.043-.65-129.399 12.499-160.375 20.998-30.976 8.498-74.11-10.714-154.38 24.496-35.319 15.493-56.272 22.17-93.927 30.497-46.52 10.286-89.93-1.5-121.406 11.998"
      ></path>
      <defs>
        <linearGradient
          id="paint0_linear_540_31"
          x1="322.5"
          x2="322.5"
          y1="1"
          y2="188"
          gradientUnits="userSpaceOnUse"
        >
          <stop stopColor="#138EED" stopOpacity="0.4"></stop>
          <stop offset="1" stopColor="#058FFB" stopOpacity="0"></stop>
        </linearGradient>
      </defs>
    </svg>
  </div>
);
}

Adding motion

The end result should look like the component below, pay attention to not only the motion, but also one detail that makes the graph feel nicer.

"use client";

import { useMotionValue, useMotionTemplate, motion } from "motion/react";

export default function Graph() {
const clipPathValue = useMotionValue(0);
const clipPathTemplate = useMotionTemplate`inset(0px ${clipPathValue}% 0px 0px)`;

function onPointerMove(e) {
  const rect = e.currentTarget.getBoundingClientRect();
  const distanceFromRight = Math.max(rect.right - e.clientX, 0);
  const percentageFromRight = Math.min(
    (distanceFromRight / rect.width) * 100,
    100,
  );
  clipPathValue.set(percentageFromRight);
}

return (
  <div className="wrapper" onPointerMove={onPointerMove}>
    <motion.svg
      xmlns="http://www.w3.org/2000/svg"
      fill="none"
      viewBox="0 0 644 188"
      onPointerMove={onPointerMove}
      style={{
        clipPath: clipPathTemplate,
      }}
    >
      <path
        stroke="#0090FF"
        strokeWidth="2"
        d="M1 118.5s82.308-15.501 113.735-29 74.769-1.713 121.217-12c37.596-8.328 58.517-15.006 93.781-30.5 80.146-35.215 123.213-16 154.141-24.5S635.97.849 644 1.5"
      ></path>
      <path
        fill="url(#paint0_linear_540_31)"
        d="M113.912 89.012C82.437 102.511 1 118.01 1 118.01V188h643V1.023c-8.043-.65-129.399 12.499-160.375 20.998-30.976 8.498-74.11-10.714-154.38 24.496-35.319 15.493-56.272 22.17-93.927 30.497-46.52 10.286-89.93-1.5-121.406 11.998"
      ></path>
      <defs>
        <linearGradient
          id="paint0_linear_540_31"
          x1="322.5"
          x2="322.5"
          y1="1"
          y2="188"
          gradientUnits="userSpaceOnUse"
        >
          <stop stopColor="#138EED" stopOpacity="0.4"></stop>
          <stop offset="1" stopColor="#058FFB" stopOpacity="0"></stop>
        </linearGradient>
      </defs>
    </motion.svg>
  </div>
);
}