Framer Motion은 React 애니메이션 라이브러리로, 간단하고 직관적인 방식으로 동적 애니메이션을 구현할 수 있습니다. 특히 motion, useMotionValue, useSpring, useTransform 같은 훅과 컴포넌트를 제공하여 강력한 애니메이션 제어가 가능합니다.
useMotionValue와 useSpring 모두 값을 변경하는 데 사용되지만, 동작 메커니즘이 다릅니다.
useMotionValue
즉각적인 값 변경
값을 변경(set()
)하면 애니메이션 없이 해당 값으로 바로 전환됩니다.
예: y.set(100)
→ 값이 즉시 100으로 설정됨.
애니메이션 제어 없음
부드러운 전환 효과를 추가하려면 별도의 트랜지션이 필요합니다.
useSpring
스프링 애니메이션
값을 변경(set()
)하면 스프링 물리 모델에 따라 부드럽게 전환됩니다.
예: y.set(100)
→ 스프링 감쇠와 가속/감속 효과를 통해 자연스러운 애니메이션이 발생.
파라미터로 제어 가능
damping
, stiffness
등을 통해 애니메이션 속도와 부드러움을 조절할 수 있습니다.
useMotionValue
vs useSpring
비교 결과특성 | useMotionValue | useSpring |
---|---|---|
값 변경 속도 | 즉각적 | 부드러운 전환 (스프링 물리 효과) |
애니메이션 설정 | 별도 설정 필요 | 기본적으로 자연스러운 애니메이션 |
사용자 경험(UX) | 다소 딱딱한 이동 | 자연스러운 전환 |
아래는 스크롤 진행도에 따라 텍스트가 위로 이동하며 나타나는 애니메이션을 구현한 예제입니다.
useMotionValue
와 useSpring
을 각각 적용한 경우를 비교해 보겠습니다.
useSpring
을 사용하는 예제"use client";
import {
motion,
useScroll,
useTransform,
useSpring,
} from "framer-motion";
import { useEffect } from "react";
export default function Page() {
// 스프링 애니메이션으로 부드러운 값 제어
const smoothY = useSpring(0, {
damping: 15, // 감쇠 강도
});
const { scrollYProgress } = useScroll({
offset: ["start start", "end end"],
});
useEffect(() => {
return scrollYProgress.on("change", (latest) => {
// 스크롤 값이 바뀔 때마다 스프링 값 업데이트
smoothY.set(latest);
});
}, [scrollYProgress, smoothY]);
// 스크롤 진행도에 따라 텍스트의 Y 위치와 투명도 조절
const textY = useTransform(scrollYProgress, [0.7, 0.75], ["100%", "0%"]);
return (
<div className="relative w-full h-[3000px]">
<div className="fixed inset-0 flex items-center justify-center">
<motion.div
className="bg-orange-400"
style={{
position: "absolute",
inset: 0,
clipPath: useTransform(scrollYProgress, (value) => `circle(${value * 100}%)`),
}}
>
<div className="h-full flex flex-col p-5 items-start justify-start text-blue-600 text-4xl font-bold overflow-hidden">
{/* 텍스트 애니메이션 */}
<div className="overflow-hidden h-[40px] mb-2">
<motion.div
style={{ y: textY }}
transition={{ duration: 1.2 }}
>
<div>Aha!</div>
</motion.div>
</div>
<div className="overflow-hidden h-[40px]">
<motion.div
style={{ y: textY }}
transition={{ duration: 1.2 }}
>
<div>You found me!</div>
</motion.div>
</div>
</div>
</motion.div>
</div>
</div>
);
}
useMotionValue
를 사용하는 예제"use client";
import {
motion,
useScroll,
useTransform,
useMotionValue,
} from "framer-motion";
import { useEffect } from "react";
export default function Page() {
// 스크롤 값을 관리하는 motionValue
const smoothY = useMotionValue(0);
// 스크롤 진행도를 감지하는 useScroll
const { scrollYProgress } = useScroll({
offset: ["start start", "end end"],
});
useEffect(() => {
return scrollYProgress.on("change", (latest) => {
// 스크롤 값이 바뀔 때 즉각적으로 값을 설정
smoothY.set(latest); // 즉각적인 값 변경
});
}, [scrollYProgress, smoothY]);
return (
<div className="relative w-full h-[3000px]">
<div className="fixed inset-0 flex items-center justify-center">
<motion.div
className="bg-orange-400"
style={{
clipPath: useTransform(smoothY, (value) => `circle(${value * 100}%)`),
}}
>
<div className="h-full flex flex-col text-blue-600 text-4xl font-bold">
<motion.div style={{ y: textY }}>Aha!</motion.div>
<motion.div style={{ y: textY }}>You found me!</motion.div>
</div>
</motion.div>
</div>
</div>
);
}
const smoothY = useSpring(0, {
damping: 15,//스크롤 진행 값을 useSpring으로 처리하여 부드러운 애니메이션 효과를 적용했습니다
});
const { scrollYProgress } = useScroll({
offset: ["start start", "end end"],//스크롤 위치를 감지하고 scrollYProgress를 활용해 값을 업데이트합니다.
});
//스크롤 진행도에 따라 텍스트가 아래에서 위로 이동하도록 useTransform으로 값을 변환합니다.
const textY = useTransform(scrollYProgress, [0.7, 0.75], ["100%", "0%"]);
//clip-path 속성을 이용해 스크롤 진행에 따라 화면이 원형으로 드러나는 효과를 추가했습니다.
clipPath: useTransform(scrollYProgress, (value) => `circle(${value * 100}%)`),
따라서, 애니메이션이 필요한 경우에는 useSpring을 사용하는 것을 권장합니다.