flemo

Transitions

내장 프리셋, 직접 만들기, 스와이프 제스처

전환은 화면 사이의 애니메이션이에요. flemo는 4개의 프리셋을 기본 포함하고, 같은 도구로 직접 만들 수도 있어요.

내장 프리셋

cupertino

iOS 스타일 가로 슬라이드. 엣지 스와이프 백 포함, 기본값

material

아래에서 위로 슬라이드. 아래로 드래그해 닫기 + 러버밴드 저항

layout

layoutId 모핑에 어울리는 가벼운 페이드

none

즉시 컷, 애니메이션 없음

<Router>에서 전역 기본값을 설정해요.

<Router defaultTransitionName="material">…</Router>

내비게이션마다 덮어쓰려면 옵션으로 넘겨요.

navigate.push("/photos/:id", { id }, { transitionName: "layout" });

직접 만들기

흔한 형태라면 createTransition(여섯 단계)을 쓰고, 모든 navigate 상태를 직접 제어하고 싶다면 createRawTransition을 써요.

createTransition

myFade.ts
import { createTransition } from "@flemo/react";

export const myFade = createTransition({
  name: "myFade",
  initial: { opacity: 0 },
  idle: { value: { opacity: 1 }, options: { duration: 0 } },
  enter: { value: { opacity: 1 }, options: { duration: 0.3 } },
  enterBack: { value: { opacity: 0 }, options: { duration: 0.3 } },
  exit: { value: { opacity: 0 }, options: { duration: 0.3 } },
  exitBack: { value: { opacity: 1 }, options: { duration: 0.3 } }
});
단계재생 시점
initial어떤 애니메이션도 시작되기 전의 초기 스타일
idle전환이 없을 때의 정지 상태
enterpush / replace 동안 들어오는 화면
exitpush / replace 동안 나가는 화면
enterBackpop 동안 들어오는 (이전) 화면
exitBackpop 동안 나가는 (현재) 화면

<Router>에 등록해요.

<Router transitions={[myFade]} defaultTransitionName="myFade">

</Router>

타입 레지스트리에도 등록하면 defaultTransitionName, transitionName이 자동완성돼요. 보통 <Router>를 마운트하는 파일 하단에 같이 두면 편해요.

App.tsx
declare module "@flemo/react" {
  interface RegisterTransition {
    myFade: "myFade";
  }
}

createRawTransition

push와 replace, pop을 양쪽 모두 다른 애니메이션으로 분리하고 싶다면 raw 팩토리를 써요.

createRawTransition({
  name: "advanced",
  initial: { x: "100%" },
  idle: { value: { x: 0 }, options: { duration: 0 } },
  pushOnEnter: { value: { x: 0 }, options: { duration: 0.4 } },
  pushOnExit: { value: { x: -50 }, options: { duration: 0.4 } },
  replaceOnEnter: { value: { x: 0 }, options: { duration: 0.2 } },
  replaceOnExit: { value: { x: 0 }, options: { duration: 0.2 } },
  popOnEnter: { value: { x: 0 }, options: { duration: 0.3 } },
  popOnExit: { value: { x: "100%" }, options: { duration: 0.3 } },
  completedOnEnter: { value: { x: 0 }, options: { duration: 0 } },
  completedOnExit: { value: { x: -50 }, options: { duration: 0 } }
});

스와이프 제스처

swipeDirection을 지정하고 options에 세 개의 핸들러를 넣으면 전환을 드래그로 구동할 수 있어요.

createTransition({
  name: "myFade",
  // …단계들…
  options: {
    swipeDirection: "x", // "x" 또는 "y"
    onSwipeStart: async () => true, // true 반환하면 스와이프 시작
    onSwipe: (event, info, ctx) => {
      const progress = Math.max(0, Math.min(100, (info.offset.x / 200) * 100));
      ctx.onProgress?.(true, progress);
      return progress;
    },
    onSwipeEnd: async (event, info, ctx) => {
      const triggered = info.offset.x > 100;
      ctx.onStart?.(triggered);
      // 화면을 최종 위치로 애니메이션
      return triggered;
    }
  }
});

핸들러는 currentScreen, prevScreen, animate(target, value, options) 함수, dragControls를 받아요. 제스처 도중에 요소를 움직이는 건 직접 처리해야 해요.

데코레이터

데코레이터는 이전 화면과 현재 화면 사이에 들어가는 레이어예요. 내장 overlay 데코레이터가 cupertino 스와이프 동안 보이는 dim 효과를 만들어요.

import { createDecorator } from "@flemo/react";

const dim = createDecorator({
  name: "dim",
  // 데코레이터가 처음 마운트될 때 시작 상태.
  initial: { opacity: 0 },
  // 활성 쪽 + PUSH/REPLACE 중인 *새* 화면의 휴지 상태. IDLE-*,
  // COMPLETED-true, POPPING-true, PUSHING-true, REPLACING-true 모두 여기에
  // 머물러요. dim이 보이면 안 되는 슬롯들이라 0으로 둬요.
  idle: { value: { opacity: 0 }, options: { duration: 0 } },
  // "뒤로 들어가는" 상태. PUSHING-false / REPLACING-false에서 idle → enter로
  // 페이드인되고, COMPLETED-false 정착 룰에서 enter 값을 그대로 들고 있어요.
  enter: { value: { opacity: 0.4 }, options: { duration: 0.3 } },
  // POPPING-false 종착. 뒤에 깔려 있던 화면이 활성으로 복귀해요. enter →
  // exit로 애니메이션. 여기서 `idle`(opacity 0)과 같게 두면 COMPLETED-true
  // 휴지 룰에 부드럽게 안착해요.
  exit: { value: { opacity: 0 }, options: { duration: 0.3 } }
});

전환에 데코레이터를 이름으로 연결해요.

createTransition({
  name: "myFade",
  // …단계들…
  options: { decoratorName: "dim", swipeDirection: "x" /* … */ }
});

<Router>에 데코레이터를 등록해요.

<Router decorators={[dim]} transitions={[myFade]}>

</Router>

상태별 완전 제어가 필요하면 createRawTransition과 똑같은 형태의 createRawDecorator를 쓰면 돼요.

On this page