페이지 전환 애니메이션 전체 revert

유저 체감 개선이 확실치 않고 오히려 버벅임 느낌이 남아있어 관련 6개
커밋 (d1764de, 1344a2f, 48f43ec, f5c5c69, 670d8ab, f63c1e0) 을 git revert.
StaggerGroup 컴포넌트 제거, Feature/Admin 페이지의 Suspense 스피너 복원,
보스 리스트의 border 구조 원복.

prefetch(7ebfe4a), backdrop-blur 제거(669b358), font-display optional
(6e2159c) 은 애니메이션 무관한 최적화라 유지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-04-22 00:46:46 +09:00
parent f63c1e06c5
commit 98b27a5fae
8 changed files with 25 additions and 80 deletions

View file

@ -1,50 +0,0 @@
import { Children } from 'react'
import { motion } from 'framer-motion'
/**
* 자식을 motion.div 감싸 순차 페이드인.
* translateY 대신 scale 쓰는 이유: Chrome translateY 애니메이션을
* 레이아웃 변경(CLS)으로 카운트하는 케이스가 있어서. scale compositor-only CLS 0.
*
* @param {number} staggerDelay - 자식 간격 ()
* @param {number} scaleFrom - 시작 scale (기본 0.97)
* @param {number} duration - 자식 애니메이션 지속시간 ()
*/
export default function StaggerGroup({
children,
className,
style,
staggerDelay = 0.08,
scaleFrom = 0.97,
duration = 0.35,
}) {
const containerVariants = {
hidden: {},
show: { transition: { staggerChildren: staggerDelay } },
}
const itemVariants = {
hidden: { opacity: 0, scale: scaleFrom },
show: {
opacity: 1,
scale: 1,
transition: { duration },
},
}
return (
<motion.div
className={className}
style={style}
variants={containerVariants}
initial="hidden"
animate="show"
>
{Children.map(children, (child, i) => (
child == null || child === false
? null
: <motion.div variants={itemVariants} key={i} style={{ willChange: 'transform, opacity' }}>{child}</motion.div>
))}
</motion.div>
)
}

View file

@ -11,7 +11,11 @@ export default function FeaturePage() {
}
return (
<Suspense fallback={null}>
<Suspense fallback={
<div className="flex items-center justify-center pt-20">
<div className="w-6 h-6 border-2 border-emerald-500 border-t-transparent rounded-full animate-spin" />
</div>
}>
<Component />
</Suspense>
)

View file

@ -49,7 +49,14 @@ export default function AdminFeaturePage() {
}
return (
<Suspense fallback={null}>
<Suspense fallback={
<div className="flex items-center justify-center pt-20">
<div
className="w-6 h-6 border-2 border-t-transparent rounded-full animate-spin"
style={{ borderColor: 'var(--accent)', borderTopColor: 'transparent' }}
/>
</div>
}>
<Component />
</Suspense>
)

View file

@ -1,6 +1,5 @@
import { useEffect, useLayoutEffect } from 'react'
import { useQuery, useQueries } from '@tanstack/react-query'
import { motion } from 'framer-motion'
import { api } from '../../../api/client'
import { useLayout } from '../../../components/pc/Layout'
import CharacterPanel from './user/CharacterPanel'
@ -71,13 +70,7 @@ export default function BossCrystal() {
const isMaxReached = currentSelectedCount >= MAX_PER_CHARACTER
return (
<motion.div
className="h-full"
initial={{ opacity: 0, scale: 0.97 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.35 }}
style={{ willChange: 'transform, opacity' }}
>
<div className="h-full">
{isLoading ? (
<div
className="rounded-2xl border p-16 text-center"
@ -124,6 +117,6 @@ export default function BossCrystal() {
</div>
</div>
)}
</motion.div>
</div>
)
}

View file

@ -1,6 +1,5 @@
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'
import Select from '../../../../components/common/Select'
import StaggerGroup from '../../../../components/common/StaggerGroup'
import { DIFFICULTIES, formatMeso } from '../admin/constants'
const LABEL_EN = { easy: 'EASY', normal: 'NORMAL', hard: 'HARD', chaos: 'CHAOS', extreme: 'EXTREME' }
@ -68,12 +67,7 @@ export default function BossSelector({ characterName, bosses, selections, onChan
}}
defer
>
<StaggerGroup
className="divide-y divide-[var(--panel-border)] px-2"
staggerDelay={0.03}
yOffset={10}
duration={0.25}
>
<div className="divide-y px-2" style={{ '--tw-divide-opacity': 1 }}>
{bosses.map((boss) => {
const availableDiffs = DIFFICULTIES.filter((d) =>
boss.difficulties.some((bd) => bd.difficulty === d.key)
@ -94,7 +88,7 @@ export default function BossSelector({ characterName, bosses, selections, onChan
return (
<div
key={boss.id}
className={`flex items-center gap-3 px-3 py-3 ${
className={`flex items-center gap-3 px-3 py-3 border-t first:border-t-0 ${
disabled ? 'pointer-events-none' : ''
}`}
style={{
@ -108,7 +102,7 @@ export default function BossSelector({ characterName, bosses, selections, onChan
className="shrink-0 w-11 h-11 rounded-lg overflow-hidden"
style={{ background: 'var(--surface-nested)' }}
>
<img src={boss.image_url || '/default.png'} alt={boss.name} loading="lazy" decoding="async" className="w-full h-full object-cover" />
<img src={boss.image_url || '/default.png'} alt={boss.name} className="w-full h-full object-cover" />
</div>
<span className="text-base font-medium leading-tight whitespace-nowrap overflow-hidden text-ellipsis">{boss.name}</span>
</div>
@ -174,7 +168,7 @@ export default function BossSelector({ characterName, bosses, selections, onChan
</div>
)
})}
</StaggerGroup>
</div>
</OverlayScrollbarsComponent>
</div>
)

View file

@ -16,7 +16,6 @@ import PointsInput from './components/PointsInput'
import WeeklyDefault from './components/WeeklyDefault'
import DatePicker from '../../../components/common/DatePicker'
import ConfirmDialog from '../../../components/common/ConfirmDialog'
import StaggerGroup from '../../../components/common/StaggerGroup'
export default function Destiny() {
const calcMode = useLiberationStore((s) => s.destinyCalcMode)
@ -70,7 +69,7 @@ export default function Destiny() {
}
return (
<StaggerGroup className="space-y-6">
<>
{/* 계산 모드 탭 */}
<div
className="max-w-3xl mx-auto flex gap-1 p-1 rounded-xl border"
@ -213,6 +212,6 @@ export default function Destiny() {
confirmText="초기화"
destructive
/>
</StaggerGroup>
</>
)
}

View file

@ -24,7 +24,6 @@ import ProgressBar from './components/ProgressBar'
import WeeklyDefault from './components/WeeklyDefault'
import DatePicker from '../../../components/common/DatePicker'
import ConfirmDialog from '../../../components/common/ConfirmDialog'
import StaggerGroup from '../../../components/common/StaggerGroup'
export default function Genesis() {
const calcMode = useLiberationStore((s) => s.genesisCalcMode)
@ -94,7 +93,7 @@ export default function Genesis() {
}
return (
<StaggerGroup className="space-y-6">
<>
{/* 계산 모드 탭 */}
<div
className="max-w-3xl mx-auto flex gap-1 p-1 rounded-xl border"
@ -239,6 +238,6 @@ export default function Genesis() {
confirmText="초기화"
destructive
/>
</StaggerGroup>
</>
)
}

View file

@ -9,7 +9,6 @@ import { formatMesoKorean } from '../../../utils/formatting'
import { formatKoreanDate, computeCompletion, TYPE_ORDER, eventBonusForType } from '../utils'
import CharacterCard from './user/CharacterCard'
import SymbolCard from './user/SymbolCard'
import StaggerGroup from '../../../components/common/StaggerGroup'
export default function Symbol() {
const { setFullscreen } = useLayout()
@ -194,7 +193,7 @@ export default function Symbol() {
}, [symbols, progress, selectedChar?.event_skill])
return (
<StaggerGroup className="space-y-6 pb-10 max-w-5xl mx-auto">
<div className="space-y-6 pb-10 max-w-5xl mx-auto">
{/* 캐릭터 조회 */}
<div
className="rounded-2xl border p-5 space-y-4"
@ -350,6 +349,6 @@ export default function Symbol() {
</div>
</div>
</div>
</StaggerGroup>
</div>
)
}