From f63c1e06c539bfcae0193af9cba5c5e96426834d Mon Sep 17 00:00:00 2001 From: caadiq Date: Wed, 22 Apr 2026 00:42:25 +0900 Subject: [PATCH] =?UTF-8?q?=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98?= =?UTF-8?q?=EC=9D=84=20translateY=20=EB=8C=80=EC=8B=A0=20scale=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=95=B4=20CLS=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- frontend/src/components/common/StaggerGroup.jsx | 17 +++++++++-------- .../features/boss-crystal/pc/BossCrystal.jsx | 6 +++--- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/common/StaggerGroup.jsx b/frontend/src/components/common/StaggerGroup.jsx index 57d7497..f590fbc 100644 --- a/frontend/src/components/common/StaggerGroup.jsx +++ b/frontend/src/components/common/StaggerGroup.jsx @@ -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 - : {child} + : {child} ))} ) diff --git a/frontend/src/features/boss-crystal/pc/BossCrystal.jsx b/frontend/src/features/boss-crystal/pc/BossCrystal.jsx index 5e871ae..8485088 100644 --- a/frontend/src/features/boss-crystal/pc/BossCrystal.jsx +++ b/frontend/src/features/boss-crystal/pc/BossCrystal.jsx @@ -73,9 +73,9 @@ export default function BossCrystal() { return ( {isLoading ? (