애니메이션을 translateY 대신 scale로 변경해 CLS 제거
Chrome DevTools의 레이아웃 변경 원인 분석 결과 애니메이션 × 3이 CLS 0.17의 주요 원인으로 지목됨. translateY 애니메이션을 레이아웃 변경으로 카운트하는 케이스가 있음. - StaggerGroup: y:30 → scale:0.97 로 변경. scale/opacity는 compositor-only 속성이라 layout/paint 없이 GPU만 사용 - BossCrystal 루트 애니메이션도 동일하게 scale 기반으로 변경 - 자식 motion.div에 will-change: transform, opacity 명시적 추가 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
669b358460
commit
f63c1e06c5
2 changed files with 12 additions and 11 deletions
|
|
@ -3,19 +3,20 @@ import { motion } from 'framer-motion'
|
|||
|
||||
/**
|
||||
* 자식을 각 motion.div 로 감싸 순차 페이드인.
|
||||
* 기본값은 프로미스나인 사이트와 동일 (y 30, duration 0.4, 간격 0.1s).
|
||||
* translateY 대신 scale 을 쓰는 이유: Chrome 이 translateY 애니메이션을
|
||||
* 레이아웃 변경(CLS)으로 카운트하는 케이스가 있어서. scale 은 compositor-only 라 CLS 0.
|
||||
*
|
||||
* @param {number} staggerDelay - 자식 간 간격 (초)
|
||||
* @param {number} yOffset - 시작 y 오프셋 (px)
|
||||
* @param {number} scaleFrom - 시작 scale (기본 0.97)
|
||||
* @param {number} duration - 각 자식 애니메이션 지속시간 (초)
|
||||
*/
|
||||
export default function StaggerGroup({
|
||||
children,
|
||||
className,
|
||||
style,
|
||||
staggerDelay = 0.1,
|
||||
yOffset = 30,
|
||||
duration = 0.4,
|
||||
staggerDelay = 0.08,
|
||||
scaleFrom = 0.97,
|
||||
duration = 0.35,
|
||||
}) {
|
||||
const containerVariants = {
|
||||
hidden: {},
|
||||
|
|
@ -23,10 +24,10 @@ export default function StaggerGroup({
|
|||
}
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { opacity: 0, y: yOffset },
|
||||
hidden: { opacity: 0, scale: scaleFrom },
|
||||
show: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
scale: 1,
|
||||
transition: { duration },
|
||||
},
|
||||
}
|
||||
|
|
@ -42,7 +43,7 @@ export default function StaggerGroup({
|
|||
{Children.map(children, (child, i) => (
|
||||
child == null || child === false
|
||||
? null
|
||||
: <motion.div variants={itemVariants} key={i}>{child}</motion.div>
|
||||
: <motion.div variants={itemVariants} key={i} style={{ willChange: 'transform, opacity' }}>{child}</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@ export default function BossCrystal() {
|
|||
return (
|
||||
<motion.div
|
||||
className="h-full"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.03 }}
|
||||
initial={{ opacity: 0, scale: 0.97 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.35 }}
|
||||
style={{ willChange: 'transform, opacity' }}
|
||||
>
|
||||
{isLoading ? (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue