skip to Main Content

I am using motion to animate a background color whenever the variable colorHex changes, which works nicely. I would also like to scale up and back down each time the color changes. For this I’ve used scale: [1, 2, 1] however because the value never changes, it only runs on the initial animation. How can I ensure it retriggers whenever colorHex changes?

<motion.div
  transition={{ duration: 0.3, delay: offset * 0.1, ease: "easeOut" }}
  animate={{
    backgroundColor: colorHex,
    scale: [1, 2, 1],
  }}
  ...

Note that the only work around I’ve found is to set the scale to a new (very slightly different) value when the color value changes.

2

Answers


  1. All you need to do is add a key prop.

    import "./styles.css";
    import { useRef, useState } from "react";
    import { motion, useScroll } from "motion/react";
    
    export default function App() {
      const ref = useRef(null);
      const offset = 1;
      const [colorHex, setColorHex] = useState("#f00");
    
      return (
        <>
          <input
            type="text"
            value={colorHex}
            onChange={(e) => setColorHex(e.target.value)}
          />
          <motion.div
            {/* add colorHex as key */}
            key={colorHex}
            transition={{ duration: 0.3, delay: offset * 0.1, ease: "easeOut" }}
            animate={{
              backgroundColor: colorHex,
              scale: [1, 2, 1],
            }}
            style={{
              height: "50vh",
              width: "50vw",
              margin: "auto",
              background: "#fff",
            }}
          ></motion.div>
        </>
      );
    }
    
    
    Login or Signup to reply.
  2. First, you can trigger "manual" animations by using useAnimate. Please note that I modified your scale to [1,2,3,1], because potentially, you would like to go back to original scale:

      const [colorHex, setColorHex] = useState(getRandomColor());
      const [scope, animate] = useAnimate();
    
      const handleClick = () => {
        setColorHex(getRandomColor());
        animate(scope.current, { scale: [1, 2, 3, 1] });
      };
    
      return (
        <motion.div
          ref={scope}
          onClick={handleClick}
          transition={{ duration: 0.3, delay: offset * 0.1, ease: "easeOut" }}
          animate={{
            backgroundColor: colorHex,
            scale: [1, 2, 1],
          }}
        >
          Hello World!
        </motion.div>
     )
    

    Now that you have control on how to trigger the animation, you can just use React.useEffect to listen for changes on your colorHex:

    export default function App() {
      const [colorHex, setColorHex] = useState(getRandomColor());
      const [scope, animate] = useAnimate();
    
      useEffect(() => {
        animate(scope.current, { scale: [1, 2, 3, 1] });
      }, [colorHex]);
    
      const handleClick = () => {
        setColorHex(getRandomColor());
      };
    
      return (
        <motion.div
          onClick={handleClick}
          ref={scope}
          transition={{ duration: 0.3, delay: offset * 0.1, ease: "easeOut" }}
          animate={{
            backgroundColor: colorHex,
            scale: [1, 2, 1],
          }}
        >
          Hello World!
        </motion.div>
      );
    }
    

    Here is a small repro with the full code.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search