Ayhan
Ayhan

Reputation: 93

Change letter color of a text based on scroll position with React and Framer Motion

I want to change the color of each letter of a text based on the scroll position.

The animation is the same as here (scroll down to the "Precision" section):

https://www.polestar.com/us/polestar-1/

I use React and Framer Motion. I don't have much yet, that's the only one so far.

https://codesandbox.io/s/epic-kare-khlt5f?file=/src/Example.js

Does anyone have a solution / idea how I can animate the individual letters?

Upvotes: 1

Views: 768

Answers (1)

taiwanHsi
taiwanHsi

Reputation: 86

App.js

import Example from "./Example";
import "./styles.css";

export default function App() {
  return (
    <div className="App">
      <Example />
    </div>
  );
}

style.css

body {
  margin: 0;
  box-sizing: border-box;
}

.section__wrapper {
  max-height: 100px;
  overflow-y: scroll;
}

.section__title {
  font-size: 72px;
}

example.js

import "./styles.css";
import { useEffect, useRef, useState } from "react";
import { useScroll, useMot } from "framer-motion";

export default function Example() {
  const targetRef = useRef(null);

  const str = "Here is some text that should be animated";
  const [a, setA] = useState(); //the string display in div
  const [myScroll, setMyScroll] = useState(0); //scrool position
  const [myColor, setMyColor] = useState(0);
  //set ref scroll position by document onScroll
  const handleScroll = () => {
    setMyScroll(targetRef.current.scrollTop);
  };

  useEffect(() => {
    //check after the ref exist
    if (targetRef.current) {
      //you can design your math
      const displayLength = Math.floor(
        ((str.length + 10) * targetRef.current.scrollTop) /
          targetRef.current.scrollHeight
      );
      const displayColor = 255-Math.floor(
        (200 * targetRef.current.scrollTop) / targetRef.current.scrollHeight
      );

      // console.log(
      //   Math.floor(
      //     (str.length * targetRef.current.scrollTop) /
      //       targetRef.current.scrollHeight
      //   )
      // );
      // console.log(Math.floor(100*targetRef.current.scrollTop /
      // targetRef.current.scrollHeight));

      //set A as the "cool string"
      setA(str.substring(0, displayLength));
      //set color as the "cool color"
      setMyColor(displayColor);
    }
  }, [myScroll]);

  return (
    <>
      <div
        className="section__wrapper"
        ref={targetRef}
        onScroll={() => {
          handleScroll();
        }}
      >
        <div>
          {/* scroll content */}
          <div className="section__title">1</div>
          <div className="section__title">2</div>
          <div className="section__title">3</div>
          <div className="section__title">4</div>
          <div className="section__title">5</div>
          <div className="section__title">6</div>
          <div className="section__title">7</div>
          <div className="section__title">8</div>
        </div>
      </div>
      {/* display the string */}
      <div
        className="section__title"
        style={{ color: `rgb(${myColor},${myColor},${myColor})` }}
      >
        {a}
      </div>
    </>
  );
}

I am not good at the css, so i let it be easier. I change your code a lots and cleaner. I just focus on the scroll top ,height and "cool string". That's it.

Upvotes: 0

Related Questions