GestureViewer Props
renderItem
renderItem은 data의 각 아이템을 렌더링하며, renderItem(item, index, { isActive, setItemDimensions }) 형태로 세 번째 인자에서 isActive와 setItemDimensions를 제공합니다.
비디오 재생처럼 활성 아이템에서만 필요한 작업은 isActive로 제어할 수 있습니다. 동작 방식과 예시는 renderItem 활성 상태 (isActive)를 참고하세요.
setItemDimensions({ width, height })는 렌더링된 콘텐츠의 원본 크기를 알 수 있게 된 뒤 호출하세요. 예시와 호출 시점은 콘텐츠 크기 가이드를 참고하세요.
panInertia (기본값: false)
확대한 이미지를 드래그하고 손을 떼면 관성으로 이동하다 감속합니다. 기본 설정은 true, 세부 설정은 enabled를 명시한 객체로 전달합니다.
<GestureViewer
data={data}
renderItem={renderItem}
panInertia
/>
<GestureViewer
data={data}
renderItem={renderItem}
panInertia={{
enabled: true,
deceleration: 0.9998,
velocityFactor: 1,
rubberBandEffect: true,
rubberBandFactor: 3,
}}
/>
type GestureViewerPanInertiaConfig = {
enabled: boolean;
deceleration?: number;
velocityFactor?: number;
rubberBandEffect?: boolean;
rubberBandFactor?: number;
};
// panInertia?: boolean | GestureViewerPanInertiaConfig
생략하거나 false, { enabled: false }를 전달하면 기존처럼 관성 없이 멈춥니다. 빈 객체는 허용하지 않으며 enablePanWhenZoomed도 켜져 있어야 합니다.
필요한 값만 지정하면 생략한 항목에는 다음 기본값을 사용합니다.
deceleration은 0보다 크고 1보다 작은 유한한 값이어야 합니다. velocityFactor와 rubberBandFactor는 0보다 큰 유한한 값이어야 합니다. 잘못된 값은 각 항목의 기본값으로 대체합니다. 설정 변경은 다음 드래그 종료부터 적용하며, 관성을 비활성화하면 현재 애니메이션을 중단하고 경계를 넘은 위치도 정리합니다.
탄성은 손을 뗀 뒤의 관성 이동에 적용됩니다. 콘텐츠 경계 안에서 멈추며 페이지를 전환하지 않습니다. 실제 콘텐츠 경계를 사용하려면 getItemDimensions 또는 setItemDimensions로 크기를 제공하세요. 없으면 뷰어 크기를 기준으로 제한합니다. 새 터치는 경계 밖이라도 잡은 위치를 유지하고, 손을 떼면 경계로 돌아옵니다. 핀치, 확대·회전·크기 변경, 페이지 이동, 닫기도 관성을 중단합니다. 실제 드래그 속도와 경계, 시스템의 동작 줄이기 설정은 뷰어 내부에서 관리하며 panInertia로 변경할 수 없습니다.
getItemDimensions
getItemDimensions(item, index)는 콘텐츠의 원본 크기를 이미 알고 있을 때 반환합니다. 우선순위와 대체 동작은 콘텐츠 크기 가이드를 참고하세요.
getItemKey
getItemKey(item, index)는 같은 논리 아이템이 같은 슬롯에서 다른 객체로 다시 만들어질 수 있을 때, 런타임에 등록한 크기를 유지하기 위한 안정적인 키를 반환합니다. 키 조건은 콘텐츠 크기 가이드를 참고하세요.
enableLoop (기본값: false)
루프 모드를 활성화합니다. true일 때 마지막 아이템에서 다음으로 가면 첫 번째로, 첫 번째에서 이전으로 가면 마지막으로 돌아갑니다.
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
<GestureViewer
data={data}
renderItem={renderItem}
enableLoop={true}
/>
);
}
windowSize (기본값: 3)
현재 아이템을 포함해 내부 render-window slot을 몇 개 마운트할지 제어합니다. 값은 최소 3 이상의 홀수로 정규화됩니다. 예를 들어 4는 5가 됩니다.
하드 최대값은 없습니다. 큰 값은 더 많은 renderItem 콘텐츠를 의도적으로 마운트하므로, 메모리, 렌더링, 이미지 디코딩 비용을 감수하는 명시적인 선택으로 다루세요.
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
<GestureViewer
data={data}
renderItem={renderItem}
windowSize={5}
/>
);
}
pageSpacing (기본값: 0)
페이지 사이의 가로 시각 간격을 설정합니다. 페이지 stride는 width + pageSpacing이며, zoom bounds는 계속 width와 height를 사용합니다.
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
<GestureViewer
data={data}
renderItem={renderItem}
pageSpacing={16}
/>
);
}
autoPlay (기본값: false)
자동 재생 모드를 활성화합니다. true일 때 지정된 간격 후 다음 아이템으로 자동으로 재생됩니다.
enableLoop이 활성화되면 마지막 아이템에서 다음으로 가면 첫 번째로, 첫 번째에서 이전으로 가면 마지막으로 돌아갑니다.
enableLoop이 비활성화되면 마지막 아이템에서 정지됩니다.
- 아이템이 하나만 있을 때 자동 재생이 비활성화됩니다.
- 줌 또는 회전 제스처가 감지되면 자동 재생이 일시 중지됩니다.
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
<GestureViewer
data={data}
renderItem={renderItem}
autoPlay={true}
/>
);
}
autoPlayInterval (기본값: 3000)
자동 재생 간격을 밀리초 단위로 설정합니다.
양수 정수여야 합니다. 값이 250ms 미만이면 런타임에서 250ms로 제한됩니다.
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
<GestureViewer
data={data}
renderItem={renderItem}
autoPlay={true}
autoPlayInterval={3000}
/>
);
}
onSingleTap
뷰어에서 싱글 탭이 확정되면 실행됩니다. 헤더, 푸터, 캡션, 액션 버튼처럼 뷰어 컨트롤 UI를 토글할 때 유용하며, 뷰어 위에 별도의 pressable을 덮지 않아도 됩니다.
- 더블 탭 줌이 활성화되어 있으면 더블 탭이 아닌지 확인한 뒤 실행되므로 약간 늦게 호출될 수 있습니다
- 스와이프, 핀치, dismiss, 더블 탭 줌 제스처에서는 호출되지 않습니다
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const [showControls, setShowControls] = useState(true);
return (
<GestureViewer
data={images}
renderItem={renderImage}
onSingleTap={() => setShowControls((prev) => !prev)}
/>
);
}
initialIndex (기본값: 0)
초기 인덱스 값을 설정할 수 있습니다.
maxZoomScale (기본값: 2)
최대 줌 배율을 제어할 수 있습니다.
GestureViewerProps interface
Go to source
GestureViewerProps
export type GestureViewerItemDimensions = Readonly<{
/** 콘텐츠의 원본 너비입니다. 유한한 양수여야 합니다. */
width: number;
/** 콘텐츠의 원본 높이입니다. 유한한 양수여야 합니다. */
height: number;
}>;
export type GestureViewerItemDimensionsResolver<ItemT> = (
item: ItemT,
index: number,
) => GestureViewerItemDimensions | undefined;
export type GestureViewerItemKey = string | number;
export type GestureViewerItemKeyResolver<ItemT> = (
item: ItemT,
index: number,
) => GestureViewerItemKey;
export type GestureViewerRenderItemInfo = {
/**
* 렌더링된 아이템이 현재 활성 상태인지 여부입니다.
* @remarks 페이지 전환 중에는 기존 아이템이 활성 상태를 유지합니다. 전환이 완료되어 다른 아이템으로 이동하면 해당 아이템이 활성화됩니다.
*/
readonly isActive: boolean;
/**
* 렌더링된 콘텐츠의 원본 크기를 사용할 수 있게 된 후 등록합니다.
* @remarks 이미지 로드 또는 이벤트 콜백에서 호출하거나, 커밋 이후의 일반 `useEffect`에서 호출하세요. 렌더링 중이나 하위 컴포넌트의 `useLayoutEffect`에서는 호출하지 마세요.
*/
readonly setItemDimensions: (dimensions: GestureViewerItemDimensions) => void;
};
export interface GestureViewerProps<ItemT> {
/**
* 여러 개의 `GestureViewer` 인스턴스를 효율적으로 관리하고 싶다면 `id` prop을 사용해 각각의 `GestureViewer`를 구분할 수 있습니다.
* @remarks `GestureViewer`는 컴포넌트가 언마운트되면 인스턴스를 메모리에서 자동으로 제거하므로 별도의 수동 메모리 관리가 필요하지 않습니다.
* @defaultValue 'default'
*/
id?: string;
/**
* `GestureViewer`에 표시할 데이터입니다.
*/
data: ItemT[];
/**
* 컴포넌트가 마운트될 때 `GestureViewer`에 처음 표시할 아이템의 인덱스입니다.
* @defaultValue 0
*/
initialIndex?: number;
/**
* `GestureViewer`가 닫힐 때 호출되는 콜백 함수입니다.
*/
onDismiss?: () => void;
/**
* dismiss 인터랙션이 시작될 때 호출되는 콜백 함수입니다.
* @remarks dismiss 제스처/애니메이션이 진행되는 동안 외부 UI(예: 헤더, 버튼)를 숨길 때 유용합니다.
*/
onDismissStart?: () => void;
/**
* 아이템을 렌더링할 때 호출되는 콜백 함수입니다.
* @param item - 렌더링할 아이템입니다.
* @param index - `data` 배열에서 아이템의 인덱스입니다.
* @param info - 마운트된 이 slot의 렌더링 상태입니다.
*/
renderItem: (item: ItemT, index: number, info: GestureViewerRenderItemInfo) => React.ReactElement;
/**
* 뷰어 콘텐츠에서 싱글 탭이 확정되었을 때 호출되는 콜백 함수입니다.
* @remarks
* - 이 콜백은 탭이 싱글 탭으로 확정된 뒤에만 실행되므로, 더블 탭 줌이 활성화되어 있으면 약간 지연될 수 있습니다.
* - 스와이프, 핀치, dismiss, 더블 탭 줌 제스처에서는 호출되지 않습니다.
* - 전체 화면 탭 처리가 필요하다면 `renderContainer`에서 pressable을 덮어씌우는 대신 이 콜백 사용을 권장합니다.
*/
onSingleTap?: (event: GestureViewerSingleTapEvent<ItemT>) => void;
/**
* 콘텐츠의 원본 크기를 이미 알고 있을 때 반환합니다.
* @remarks 크기를 아직 모르면 `undefined`를 반환하세요. 유효하지 않은 크기는 뷰어 셀 크기로 대체됩니다.
*/
getItemDimensions?: GestureViewerItemDimensionsResolver<ItemT>;
/**
* 같은 아이템이 같은 슬롯에서 다른 객체로 다시 만들어질 수 있을 때 사용할 안정적인 논리 키를 반환합니다.
* @remarks 객체 동일성이 리렌더링 사이에 유지되지 않을 때 사용하세요. 이 키는 같은 렌더 결과를 가리켜야 하며, 이미지 소스나 원본 크기가 바뀌면 함께 바뀌어야 합니다. 배열 인덱스만 그대로 반환하지 마세요. 아이템이 다른 위치로 이동하면 새 슬롯에서 크기를 다시 등록할 때까지 `getItemDimensions` 또는 뷰어 셀 크기를 사용합니다.
*/
getItemKey?: GestureViewerItemKeyResolver<ItemT>;
/**
* 컨테이너를 렌더링할 때 호출되는 콜백 함수입니다.
* @remarks 뷰어 주변에 추가 UI(예: 닫기 버튼, 툴바)를 구성할 때 유용합니다.
* 전체 화면 탭 처리는 뷰어 콘텐츠 위에 pressable을 덮어씌우기보다 `onSingleTap` 사용을 권장합니다.
* 두 번째 인자는 `dismiss()`처럼 뷰어를 닫을 수 있는 제어 헬퍼를 제공합니다.
*
* @param children - 컨테이너 내부에 렌더링할 뷰어 콘텐츠입니다.
* @param helpers - 뷰어 제어 헬퍼입니다. 현재는 `dismiss()`를 포함합니다.
* @returns 전달된 `children`을 감싸서 렌더링하는 React 엘리먼트입니다.
*/
renderContainer?: (
children: React.ReactElement,
helpers: { dismiss: () => void },
) => React.ReactElement;
/**
* `GestureViewer`의 너비입니다.
* @remarks 이 prop을 지정하지 않으면 `GestureViewer`의 너비는 화면 너비와 동일해집니다.
* @defaultValue 화면 너비
*/
width?: number;
/**
* `GestureViewer`의 높이입니다.
* @remarks 이 prop을 지정하지 않으면 `GestureViewer`의 높이는 화면 높이와 동일해집니다.
* @defaultValue 화면 높이
*/
height?: number;
/**
* backdrop의 스타일입니다.
*/
backdropStyle?: StyleProp<ViewStyle>;
/**
* 컨테이너의 스타일입니다.
*/
containerStyle?: StyleProp<ViewStyle>;
/**
* 자동 재생 모드입니다.
* @remarks
* - `true`이면 지정된 간격 후 다음 아이템이 자동으로 재생됩니다.
* - `enableLoop`가 활성화되어 있으면 마지막 아이템 다음에 첫 번째 아이템으로 돌아갑니다.
* - `enableLoop`가 비활성화되어 있으면 마지막 아이템에서 멈춥니다.
* - 아이템이 하나뿐이면 자동 재생은 비활성화됩니다.
* - 줌 또는 회전 제스처가 감지되면 자동 재생이 일시 정지됩니다.
* @defaultValue false
*/
autoPlay?: boolean;
/**
* 자동 재생 간격입니다.
* @remarks
* - `autoPlay`가 활성화되어 있으면 지정된 간격(ms)마다 다음 아이템으로 이동합니다.
* - 양의 정수여야 합니다. 250ms 미만 값은 런타임에서 250ms로 보정됩니다.
* @defaultValue 3000
*/
autoPlayInterval?: number;
/**
* dismiss 제스처 옵션입니다.
* @remarks 위/아래 방향을 선택할 수 있는 수직 스와이프 닫기 제스처에 유용합니다.
*/
dismiss?: {
/**
* `false`이면 dismiss 제스처가 비활성화됩니다.
* @defaultValue true
*/
enabled?: boolean;
/**
* 어떤 수직 스와이프 방향에서 `onDismiss`를 호출할지 설정합니다.
* @remarks 기존 동작과 호환되는 `down`, 위로만 닫는 `up`, 양방향 모두 허용하는 `both`를 사용할 수 있습니다.
* @defaultValue 'down'
*/
direction?: GestureViewerDismissDirection;
/**
* `threshold`는 수직 제스처 중 임계값을 적용해 `onDismiss`가 호출되는 시점을 제어합니다.
* @defaultValue 80
*/
threshold?: number;
/**
* `resistance`는 dismiss 제스처 중 저항을 적용해 수직 이동 범위를 제어합니다.
* @defaultValue 2
*/
resistance?: number;
/**
* 기본적으로 설정된 dismiss 방향으로 드래그하는 동안 배경 `opacity`가 점진적으로 감소합니다.
* @remarks `false`이면 이 애니메이션이 비활성화됩니다.
* @defaultValue true
*/
fadeBackdrop?: boolean;
};
/**
* 가로 스와이프 제스처 옵션입니다.
* @remarks 사용자가 직접 수행하는 좌우 페이지 스와이프 제스처를 제어합니다. 비활성화해도 controller 이동과 autoplay는 계속 사용할 수 있습니다.
*/
horizontalSwipe?: {
/**
* `false`이면 사용자가 직접 수행하는 가로 페이지 스와이프 제스처가 비활성화됩니다.
* @defaultValue true
*/
enabled?: boolean;
/**
* 뷰어 너비에 대한 거리 임계값 비율입니다.
* @remarks 절대 가로 이동 거리가 `width * distanceThresholdRatio`보다 엄격히 클 때, 또는 속도가 `velocityThreshold`를 넘을 때 페이지가 전환됩니다. `0`과 `1`보다 큰 유한한 값은 허용되며, 음수와 유한하지 않은 값은 기본값으로 대체됩니다.
* @defaultValue 0.25
*/
distanceThresholdRatio?: number;
/**
* 초당 points 단위의 가로 속도 임계값입니다.
* @remarks 절대 가로 속도가 `velocityThreshold`보다 엄격히 클 때, 또는 거리가 `distanceThresholdRatio`를 넘을 때 페이지가 전환됩니다. `0`과 모든 유한한 0 이상의 값은 허용되며, 음수와 유한하지 않은 값은 기본값으로 대체됩니다.
* @defaultValue 800
*/
velocityThreshold?: number;
};
/**
* 줌이 활성화된 경우에만 동작하며, 확대된 상태에서 아이템 위치를 이동할 수 있게 합니다.
* @remarks `false`이면 줌 중 제스처 이동이 비활성화됩니다.
* @defaultValue true
*/
enablePanWhenZoomed?: boolean;
/**
* 확대된 이미지를 드래그하고 손을 떼면 관성을 적용합니다.
* @remarks `enablePanWhenZoomed`가 활성화되어 있어야 합니다. 기본 설정은 `true`, 사용자 설정은 `enabled: true`를 포함한 객체로 전달합니다. 콘텐츠 경계와 시스템의 동작 줄이기 설정을 따릅니다.
* @defaultValue false
*/
panInertia?:
| boolean
| {
/** 설정 객체를 사용할 때 관성 활성화 여부를 명시합니다. */
enabled: boolean;
/**
* 속도가 줄어드는 정도입니다. `1`에 가까울수록 오래 미끄러집니다.
* @remarks `0`보다 크고 `1`보다 작은 유한한 값만 허용합니다. 잘못된 값은 기본값으로 대체합니다.
* @defaultValue 0.9997
*/
deceleration?: number;
/**
* 손을 뗄 때의 속도에 적용해 이동량을 조절하는 배율입니다.
* @remarks `0`보다 큰 유한한 값만 허용합니다. 잘못된 값은 기본값으로 대체합니다.
* @defaultValue 0.5
*/
velocityFactor?: number;
/**
* 콘텐츠 경계를 잠깐 넘었다가 가장자리로 돌아오는 탄성을 적용합니다.
* @defaultValue true
*/
rubberBandEffect?: boolean;
/**
* 경계를 넘은 뒤 돌아오는 힘의 크기입니다. `rubberBandEffect`가 false이면 무시합니다.
* @remarks `0`보다 큰 유한한 값만 허용합니다. 잘못된 값은 기본값으로 대체합니다.
* @defaultValue 1
*/
rubberBandFactor?: number;
};
/**
* 두 손가락 핀치 제스처를 제어합니다.
* @remarks `false`이면 두 손가락 줌 제스처가 비활성화됩니다.
* @defaultValue true
*/
enablePinchZoom?: boolean;
/**
* 더블 탭 줌 제스처를 제어합니다.
* @remarks `false`이면 더블 탭 줌 제스처가 비활성화됩니다.
* @defaultValue true
*/
enableDoubleTapZoom?: boolean;
/**
* 무한 루프 탐색을 활성화합니다.
* @defaultValue false
*/
enableLoop?: boolean;
/**
* 현재 아이템을 포함해 마운트할 render-window 슬롯 수입니다.
* @remarks 값은 최소 3 이상의 홀수로 정규화됩니다. 예를 들어 `4`는 `5`가 됩니다.
* @defaultValue 3
*/
windowSize?: number;
/**
* 페이지 사이의 가로 시각 간격입니다.
* @remarks 페이지 stride는 `width + pageSpacing`이며, zoom bounds는 계속 `width`와 `height`를 사용합니다.
* @defaultValue 0
*/
pageSpacing?: number;
/**
* 최대 줌 배율입니다.
* @defaultValue 2
*/
maxZoomScale?: number;
/**
* 트리거 기반 애니메이션 설정입니다.
* @remarks 애니메이션 지속 시간, easing, 시스템 reduce-motion 동작을 커스터마이즈할 수 있습니다.
*
* @example
* ```tsx
* <GestureViewer
* triggerAnimation={{
* duration: 250,
* easing: Easing.out(Easing.cubic),
* reduceMotion: 'system',
* onAnimationComplete: () => {
* console.log('애니메이션 완료');
* },
* }}
* />
* ```
*/
triggerAnimation?: TriggerAnimationConfig;
}