기록을 불러오는 중입니다...
ViewTransition 컴포넌트를 발견했습니다.React.ViewTransition 훑어보기ViewTransition 컴포넌트는 매우 간단하게 사용할 수 있습니다.import { ViewTransition } from "react";
<ViewTransition>
<div>...</div>
</ViewTransition>ViewTransition 컴포넌트는 enter, exit, default 등의 다양한 프로퍼티를 제공합니다. 뷰 트랜지션이 실행될 때, 인자로 넘겨준 클래스 네임을 자식 컴포넌트에 자동으로 추가해줍니다."default", "none", "auto" 값을 지원하며, 커스텀 클래스 네임도 적용할 수 있습니다.<ViewTransition/> 컴포넌트는 해당 경계가 애니메이션에 참여해야 하는 시점에 가장 가까운 자식의 인라인 스타일에 view-transition-name 을 설정합니다. 그리고 내부적으로 startViewTransition 함수를 호출하여 트랜지션을 트리거합니다. 따라서, 개발자가 직접 호출할 필요는 없습니다./* 키프레임 정의 */
@keyframes move-out {
from {
transform: translateY(0%);
}
to {
transform: translateY(-100%);
}
}
@keyframes move-in {
from {
transform: translateY(100%);
}
to {
transform: translateY(0%);
}
}
/* 뷰 트랜지션 정의 */
::view-transition-old(.move) {
animation: 0.4s ease-in both move-out;
}
::view-transition-new(.move) {
animation: 0.4s ease-in both move-in;
}<ViewTransition default="move">
...
</ViewTransition>addTransitionType 훅을 이용하면 다양한 트랜지션을 구현할 수 있습니다.<ViewTransition
default={{
"navigation-back": "slide-right",
"navigation-forward": "slide-left",
}}
>
...
</ViewTransition>;
// 트랜지션 트리거
startTransition(() => {
addTransitionType("navigation-" + navigationType);
});
prefers-reduced-motion 미디어 쿼리를 활용할 수 있습니다. 기본 뷰 트랜지션 클래스를 사용하는 경우, JavaScript로 제어해야 합니다.const reducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)",
).matches;
<ViewTransition default={reducedMotion ? "none" : "auto"}>...</ViewTransition>::view-transition-old(.slide) {
animation: slide-out 0.3s ease-in-out;
}
::view-transition-new(.slide) {
animation: slide-in 0.3s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
::view-transition-old(.slide),
::view-transition-new(.slide) {
animation: none !important;
}
}<ViewTransition default="slide">
...
</ViewTransition>recipes.map((recipe) => (
<ViewTransition key={recipe.id} default="auto">
<RecipeCard>...</RecipeCard>
</ViewTransition>
));// app/foo/page.tsx
import Link from "next/link";
import { ViewTransition } from "react";
export default function FooPage() {
return (
<ViewTransition enter="slide-right" exit="slide-left">
<div>
FooPage
<div>
<Link href="/bar">➡️</Link>
</div>
</div>
</ViewTransition>
);
}
// app/foo/page.tsx
import Link from "next/link";
import { ViewTransition } from "react";
export default function BarPage() {
return (
<ViewTransition enter="slide-left" exit="slide-right">
<div>
BarPage
<div>
<Link href="/foo">⬅️</Link>
</div>
</div>
</ViewTransition>
);
}
/* app/globals.css */
@keyframes slide-in-left {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slide-out-left {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(-100%);
opacity: 0;
}
}
@keyframes slide-in-right {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slide-out-right {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
::view-transition-group(.slide-left) {
border: 1px solid red;
}
::view-transition-old(.slide-left) {
animation: slide-out-left 0.3s ease-in-out;
}
::view-transition-new(.slide-left) {
animation: slide-in-left 0.3s ease-in-out;
}
::view-transition-group(.slide-right) {
border: 1px solid blue;
}
::view-transition-old(.slide-right) {
animation: slide-out-right 0.3s ease-in-out;
}
::view-transition-new(.slide-right) {
animation: slide-in-right 0.3s ease-in-out;
}enter , exit 애니메이션을 구분해서 정의해두면 더욱 자연스럽게 화면을 전환할 수 있습니다. (이해를 돕기 위해 border 스타일을 적용했습니다.)React.ViewTransition을 사용하면서도 몇 가지 아쉬운 점이 있었습니다. React.ViewTransition과 SSGOI 모두, DOM 요소의 변화를 감지하여 트랜지션을 실행합니다.React.ViewTransition의 경우, 컴포넌트가 감싸는 자식 요소의 DOM 변화(언마운트/마운트, 리렌더링 등)를 감지하여 트랜지션을 적용합니다.id를 가진 <SsgoiTransition/> 컴포넌트 간의 전환을 감지하여 트랜지션을 실행합니다.<SsgoiTransition/> 가 언마운트 되고 마운트 되는 과정에서 트랜지션이 정상적으로 동작하는 것으로 보였습니다. 레이아웃에 트랜지션 컴포넌트를 넣었더니, 레이아웃이 공유되는 페이지 간에는 트랜지션이 정상적으로 동작하지 않았거든요. 그래서 레이아웃을 공유하지 않도록 라우트 그룹을 분리했습니다.React.ViewTransition을 이용하여 제로 의존성으로 구현하고 싶었습니다. 하지만 위에서 언급한 대로, 여러모로 귀찮은 것들이 있었습니다. 반면 SSGOI를 활용하니 비교적 간편하게 원하는 효과를 구현할 수 있었습니다.