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.
4
First, you can trigger “manual” animations by using useAnimate
:
const [colorHex, setColorHex] = useState(getRandomColor());
const [scope, animate] = useAnimate();
const handleClick = () => {
setColorHex(getRandomColor());
animate(scope.current, { scale: [1, 2, 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, 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.
0
Updated answer as per comments.
Use a combination of useAnimate
and useEffect
to animate on hex change as follows.
import "./index.css";
import { useEffect, useState } from "react";
import { motion, useAnimate } from "motion/react";
export default function App() {
const offset = 1;
const [colorHex, setColorHex] = useState("#f00");
const [scope, animate] = useAnimate<HTMLDivElement>();
useEffect(() => {
animate(scope.current, { scale: [1, 2, 1] });
}, [animate, colorHex, scope]);
return (
<>
<input
type="text"
value={colorHex}
onChange={(e) => setColorHex(e.target.value)}
/>
<motion.div
ref={scope}
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>
</>
);
}
Documentation link – https://motion.dev/docs/react-use-animate
5