Vanilla Disintegratev1.6.1
KO
이 페이지에서

Disintegrator는 하나의 완전한 시각 설정, 선택적 오디오, 캡처, 레이아웃 애니메이션, 백그라운드 준비와 보관 노드 저장소를 연결합니다.

엔트리 포인트

엔트리런타임 구성추가 의존성
vanilla-disintegrate/coreDisintegrator, 효과 및 프리셋 헬퍼와 모든 공개 타입없음
vanilla-disintegrate/particles파티클 팩토리, 시각 프리셋과 파티클 타입없음
vanilla-disintegrate/sounds내장 오디오 파일 네 개의 URL과 사운드 타입없음
vanilla-disintegrateDisintegrator, 완전한 프리셋 네 개, 파티클, 사운드, 헬퍼와 공개 타입없음
vanilla-disintegrate/snapdomSnapDOM을 기본 캡처 어댑터로 설정한 루트 API@zumer/snapdom

루트 및 /snapdom 엔트리는 완성된 내장 프리셋 네 개를 등록합니다. /snapdom만 캡처 어댑터를 제공하며, /core는 내장 프리셋, 파티클, 내장 사운드 파일과 캡처 없이 수명 주기만 제공합니다. 어떤 엔트리도 기본 프리셋을 선택하지 않습니다. 파티클, 캡처, 오디오 페이지에서 각 전용 export를 자세히 설명합니다.

생성자

import Disintegrator, { createParticleEffect } from 'vanilla-disintegrate/snapdom';

const ready = new Disintegrator({ preset: 'dust' });
const custom = new Disintegrator({ effect: createParticleEffect() });

preset과 effect 중 정확히 하나가 필요합니다.

type DisintegratorOptions =
  | { preset: PresetSelection; effect?: never; sound?: false; fallback?: FallbackEffectDefinition }
  | { preset?: never; effect: EffectDefinition; sound?: false | SoundSelection; fallback?: FallbackEffectDefinition };

프리셋은 시각 효과와 오디오를 합친 원자적 선택입니다. sound: false로 음소거할 수 있지만 다른 시각 효과나 대체 오디오와 조합할 수 없습니다. 사용자 효과는 완전한 remove 및 restore 단계를 가진 객체이며 사운드 쌍을 제공하지 않으면 무음입니다.

공통 옵션:

옵션타입기본값역할
presetsRecord<string, PresetDefinition>{}preset으로 선택할 완성된 사용자 프리셋을 추가
layoutboolean | LayoutOptionstrue삭제 후 이웃 요소를 새 위치로 이동
preparationboolean | PreparationOptionsvisible-idle등록된 요소의 스냅샷을 준비
audioPreparationfalse | 'immediate' | 'idle' | AudioPreparationOptions'idle'준비와 디코딩 오디오 소유권 예산을 제어
audioWaitTimeoutnumber | false1500소리 없이 계속하기 전 최대 오디오 대기 시간
respectReducedMotionbooleantrue사용자가 모션 감소를 요청하면 애니메이션을 건너뜀
fallbackFallbackEffectDefinition—WebGL2 렌더러를 사용할 수 없을 때 단계를 무음으로 대체
captureSnapshotCaptureSnapDOM¹스냅샷 기반 효과를 위해 요소를 canvas로 변환
overlayRootHTMLElement | () => HTMLElementdocument.body고정 위치 애니메이션 오버레이를 받음
zIndexnumber2147483646오버레이 스택 레벨을 설정
random() => numberMath.random작업마다 재현 가능한 의사 난수 스트림을 초기화
snapdomSnapdomCaptureOptions—SnapDOM의 toCanvas() 설정, /snapdom에서만 사용
onTrigger(context) => void—검증 뒤 캡처 전에 실행
onStart(context) => void—DOM 변경 뒤 재생이 시작될 때 실행
onComplete(context) => void—시각 단계가 정상적으로 끝난 뒤 실행
onError(error, context) => void—격리된 캡처, 애니메이션, 사운드와 콜백 오류를 받음

¹ SnapDOM은 /snapdom에서만 기본값입니다. 선택한 효과에 스냅샷이 필요하면 루트 및 /core 엔트리에 capture를 제공해야 합니다.

백그라운드 스냅샷 오류는 context.operation === 'prepare'를, 작업 오류는 remove 또는 restore를 보고합니다.

LayoutOptions

레이아웃 애니메이션은 FLIP 기법으로 삭제 후 영향을 받은 요소를 이전 위치에서 새 위치로 이동합니다.

옵션타입기본값역할
enabledbooleantrue다른 기본값을 유지하면서 레이아웃 애니메이션 활성화
durationnumber300리플로 애니메이션 시간(밀리초)
easingstring'cubic-bezier(0.22, 1, 0.36, 1)'WAAPI에 전달할 CSS timing function
containerHTMLElement | (element) => HTMLElement | null대상의 부모좌표계와 선택적 높이 애니메이션 대상을 정의
siblings'following' | 'all' | (element, container) => HTMLElement[]'following'참여할 자식 요소 선택
animateContainerbooleantrue변경된 컨테이너 높이에 애니메이션 적용

layout: false를 전달하면 리플로 애니메이션이 비활성화됩니다. layout은 remove()에만 적용됩니다. 복원 시 삽입된 요소는 최종 레이아웃 위치에서 애니메이션됩니다. 음수 duration은 0으로, 유한하지 않은 값은 기본값으로 정규화됩니다.

PreparationOptions

옵션타입기본값역할
strategy'immediate' | 'idle' | 'visible-idle''visible-idle'등록된 요소의 캡처 허용 시점 선택
shouldPrepare(element) => boolean() => true예약 전에 요소 필터링
rootElement | Document | nullnullIntersection Observer 루트
rootMarginstring'300px 0px'요소가 뷰포트에 들어오기 전에 visible-idle 작업 시작
concurrencynumber1최대 동시 캡처 수. 1에서 8 사이 정수로 정규화
invalidateOnResizebooleantrue크기가 바뀌면 준비된 스냅샷 무효화
observeMutationsbooleanfalse하위 트리 변경과 자손 로드 이벤트 감시
idleTimeoutnumber750requestIdleCallback 최대 대기 시간(밀리초)
fallbackDelaynumber250idle callback을 사용할 수 없을 때의 지연
scrollSettlenumber120스크롤 후 백그라운드 작업 전 대기 시간
animationSettlenumber400캡처 전 활성 요소 애니메이션의 최대 대기 시간
cachePixelBudgetnumber8_000_000canvas 물리 픽셀 단위의 LRU 용량

preparation: false는 자동 준비를 비활성화하지만 명시적 prepare() 호출은 막지 않습니다. 음수인 시간과 캐시 예산은 0으로, 유한하지 않은 값은 각 기본값으로 정규화됩니다.

AudioPreparationOptions

옵션타입기본값역할
strategy'immediate' | 'idle''idle'네이티브 오디오를 가져오고 디코딩할 시점 선택
soundsSoundPreparationSelection인스턴스 사운드인스턴스 사운드 대신 준비할 사운드 선택
cacheByteBudgetnumber8 * 1024 * 1024인스턴스별 디코딩 PCM 데이터의 LRU 소유권 예산

audioPreparation: false는 자동 작업을 비활성화합니다. 명시적 오디오 준비는 계속 사용할 수 있습니다. 음수 캐시 예산은 0으로 정규화됩니다.

완성된 프리셋

import Disintegrator, { createParticleEffect, definePreset } from 'vanilla-disintegrate/snapdom';

const dissolve = definePreset({
  effect: createParticleEffect({
    remove: { curve: 'float' },
    restore: { curve: 'settle' },
  }),
  sound: {
    remove: { src: '/audio/out.ogg', volume: 0.4 },
    restore: { src: '/audio/in.ogg', volume: 0.4 },
  },
});

const effects = new Disintegrator({
  preset: 'dissolve',
  presets: { dissolve },
});

definePreset()은 프리셋, 시각 단계와 네이티브 사운드 옵션 객체를 동결하지만 호출자가 소유한 바이너리 오디오 데이터는 동결하지 않습니다.

const builtInPresets: Readonly<Record<'dust' | 'scatter' | 'vapor' | 'wind', Readonly<PresetDefinition>>>;

builtInPresets는 루트와 /snapdom 엔트리에서 내보냅니다. 각 값은 파티클과 오디오를 합친 완전하고 동결된 프리셋입니다. /particles 엔트리는 시각 설정만 포함한 particlePresets를 대신 내보냅니다.

사용자 효과 계약

import { defineEffect } from 'vanilla-disintegrate/core';

interface EffectPhase {
  requires?: 'webgl2';
  needsSnapshot?: boolean;
  animate: AnimationFactory;
}

interface EffectDefinition {
  remove: EffectPhase;
  restore: EffectPhase;
}

const effect = defineEffect({
  remove: { needsSnapshot: false, animate: removeFactory },
  restore: { needsSnapshot: false, animate: restoreFactory },
});

needsSnapshot의 기본값은 true입니다. defineEffect()는 효과와 두 단계 객체를 동결하면서 TypeScript가 추론한 좁은 타입을 보존합니다.

WebGL2 폴백 효과

createParticleEffect()와 내장 프리셋은 단계에 requires: 'webgl2'를 자동으로 표시합니다. 라이브러리는 캡처 전에 WebGL2를 확인합니다. WebGL2가 꺼져 있거나 사용할 수 없으면 fallback이 기본 단계를 대신 실행하므로 불필요한 Canvas 스냅샷을 만들지 않습니다.

import Disintegrator, { defineEffect } from 'vanilla-disintegrate/snapdom';

const fade = defineEffect({
  remove: {
    needsSnapshot: false,
    animate: ({ element }) => element.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 180 }),
  },
  restore: {
    needsSnapshot: false,
    animate: ({ element }) => element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 180 }),
  },
});

const effects = new Disintegrator({ preset: 'dust', fallback: fade });

폴백은 프리셋뿐 아니라 requires: 'webgl2'를 선언한 모든 사용자 효과에도 적용됩니다. CSS, WAAPI, Canvas 2D, SVG 또는 다른 렌더러를 사용할 수 있으며 기본적으로 무음입니다. TypeScript는 fallback에 또 다른 WebGL2 단계를 전달하지 못하게 하고, JavaScript 구성에도 런타임에서 같은 검증을 적용합니다. 사전 확인 뒤에도 파티클 렌더러가 시작하지 못하면 라이브러리는 이미 만든 스냅샷 뒤에 폴백을 시도합니다.

애니메이션 팩토리는 AnimationContext를 받습니다.

필드타입의미
operation'remove' | 'restore'현재 단계
elementHTMLElement애플리케이션의 원본 요소
layerHTMLElement라이브러리가 소유하는 비어 있고 위치가 지정된 오버레이
snapshotHTMLCanvasElement | null원본 캡처. 스냅샷 없는 단계에서는 null
visualHTMLCanvasElement | null필요할 때 생성되는 snapshot의 전체 크기 복사본
boundsDOMRectReadOnly오버레이가 사용하는 최종 뷰포트 위치와 크기
signalAbortSignal취소 또는 인스턴스 제거 시 중단
reducedMotionfalse모션 감소 요청은 팩토리 실행 전에 건너뜀
random() => number작업별 의사 난수 스트림
addCleanup(callback: () => void) => void완료, 취소, 실패에 모두 실행할 정리 함수 등록

AnimationFactory는 Web Animation, 임의의 PromiseLike, AnimationPlayback 컨트롤러를 반환하거나 애니메이션을 만들 수 없을 때 null을 반환할 수 있습니다.

interface AnimationPlayback {
  finished: PromiseLike<unknown>;
  element?: HTMLElement;
  duration?: number;
  layoutDelay?: number;
  cancel?(): void;
  dispose?(): void;
}

element에 아직 부모가 없다면 layer에 추가됩니다. duration은 네이티브 오디오를 맞추고, layoutDelay는 삭제 리플로를 지연하며, cancel()은 재생을 멈추고, dispose()는 모든 종료 경로에서 렌더러 리소스를 해제합니다.

remove(target, options?)

해석된 요소를 분리하고 EffectOperation을 동기적으로 반환합니다.

remove(target: EffectTarget, options?: RemoveOptions): EffectOperation;
const operation = effects.remove('.card', {
  preset: 'scatter',
  sound: false,
  retain: true,
  layout: { duration: 260 },
  detach: (element) => element.remove(),
});

target은 HTMLElement 또는 선택자 문자열입니다. 선택자를 찾지 못하면 작업 시작 전에 오류를 던집니다.

detach는 기본 element.remove() 커밋을 대체합니다. 반응형 렌더러가 자체 상태로 노드를 제거해야 할 때 사용하세요. 콜백이 오류를 던지면 onError로 전달하고 네이티브 제거를 안전한 폴백으로 사용합니다.

restore(target, options?)

연결되고 측정 가능한 요소를 현재 지오메트리에서 애니메이션합니다.

restore(target: EffectTarget, options?: RestoreOptions): EffectOperation;
const operation = effects.restore(element, {
  preset: 'scatter',
  sound: false,
});

요소가 분리됐거나 너비 또는 높이가 0이면 오류를 던집니다.

두 작업 모두 하나의 완성된 preset, 해당 단계의 선택적 사운드 정의와 함께 사용자 effect, 또는 아무것도 전달하지 않아 인스턴스나 보관 노드의 표현을 상속할 수 있습니다. 상속 오디오와 프리셋 오디오는 sound: false로만 음소거할 수 있습니다.

작업 옵션

옵션적용 대상의미
preset두 작업등록된 이름 또는 인라인 완전 프리셋 선택
effect두 작업완전한 사용자 효과 선택. preset과 함께 사용할 수 없음
sound두 작업명시적 사용자 효과에 현재 단계 사운드를 추가하거나 false로 음소거
onTrigger두 작업캡처 전 작업 콜백 추가
onStart두 작업DOM 커밋 후 작업 콜백 추가
onComplete두 작업시각 효과의 정상 완료 후 작업 콜백 추가
onError두 작업격리된 오류 콜백 추가
retain삭제분리된 원본을 operation.removalId로 저장
layout삭제이번 삭제의 레이아웃 애니메이션 활성화 또는 재정의
detach삭제기본 element.remove() 커밋 대체

작업 콜백은 인스턴스 콜백과 함께 추가로 실행됩니다. preset과 effect를 모두 생략하면 표현을 상속합니다. 상속된 사운드와 프리셋 사운드는 음소거할 수 있지만 독립적으로 교체할 수 없습니다.

보관 요소

const element = effects.take(removalId); // HTMLElement | null
const discarded = effects.discard(removalId); // boolean
const count = effects.discardAll(); // number

take()은 저장 항목을 소비합니다. discard()와 discardAll()은 콘텐츠를 삽입하지 않고 참조를 해제합니다.

스냅샷 준비

const unregister = effects.register(elements);
await effects.prepare(elements);
effects.invalidate(elements);
effects.clearPrepared();

invalidate()는 context.invalidate를 통해 준비된 canvas와 캡처 엔진을 함께 갱신합니다. 명시적인 prepare()도 갱신을 요청하며 clearPrepared() 후 다음 캡처는 새로 갱신해야 합니다. DOM 관찰자가 감지할 수 없는 CSSOM 변경도 포함됩니다. 자체 캐시가 있는 사용자 정의 어댑터는 context.invalidate를 처리해야 합니다.

register()는 설정된 전략을 따릅니다. 백그라운드 준비가 꺼져 있어도 prepare()는 즉시 캡처합니다. 같은 요소의 반복 등록은 참조 카운트를 사용하며 각 해제 함수는 자신의 등록만 해제합니다.

register()는 멱등인 () => void를 반환하고, prepare()는 Promise<void>를 반환하며, invalidate()와 clearPrepared()는 void를 반환합니다.

오디오 준비

await effects.prepareAudio({
  remove: { src: 'dust' },
  restore: { src: 'dust', reverse: true },
});
effects.discardPreparedAudio();
effects.clearPreparedAudio();

인수가 없으면 prepareAudio()와 discardPreparedAudio()는 인스턴스에 설정한 사운드 쌍을 사용합니다. 자동 준비가 꺼져 있어도 명시적 메서드는 동작합니다. 디코딩 버퍼는 제한된 인스턴스 소유권 LRU를 사용하고 요청, 버퍼와 AudioContext는 인스턴스 간 공유합니다.

prepareAudio()는 Promise<void>를 반환하고, discardPreparedAudio()는 해제한 인스턴스 소유 디코딩 항목 수를 반환하며, clearPreparedAudio()는 void를 반환합니다.

destroy()

effects.destroy();

활성 작업을 취소하고 관찰자와 백그라운드 작업을 중지하며 스냅샷과 디코딩 오디오 소유권을 해제하고 보관 노드를 비웁니다. 마지막 소유자가 파괴되면 공유 재생 컨텍스트가 닫힙니다. destroy()를 반복 호출해도 안전하며 이후 새 작업을 시작하면 오류를 던집니다.

EffectOperation

interface EffectOperation {
  readonly operation: 'remove' | 'restore';
  readonly removalId: RemovalId | null;
  readonly finished: Promise<{
    operation: 'remove' | 'restore';
    status: 'completed' | 'cancelled' | 'skipped' | 'rejected';
    removalId: RemovalId | null;
  }>;
  cancel(): void;
}

핸들은 요소를 노출하지 않고 완료된 뒤 참조를 해제합니다.

상태콘텐츠 동작finished 완료 시점
completed커밋됨, 시각 단계가 정상적으로 완료시각 정리 후
cancelled커밋됨, 시각 또는 오디오 재생이 중지취소 정리 후
skipped실행 가능한 시각 단계 없이 커밋폴백 정리 후
rejected시작하지 않음, DOM이 변경되지 않음현재 소유자가 끝난 뒤

콜백과 우선순위

인스턴스 콜백은 모든 작업에 실행되고 작업 콜백은 그와 함께 실행됩니다. 명시적 작업 표현, 보관 노드가 기억한 표현, 인스턴스 설정 순서로 우선합니다. 이미 사용 중인 요소에 대한 동시 호출은 현재 소유자가 끝난 뒤 rejected로 완료됩니다.

interface EffectContext {
  operation: 'remove' | 'restore';
  element: HTMLElement;
  overlay: HTMLElement | null;
  removalId: RemovalId | null;
}

interface EffectErrorContext {
  operation: 'remove' | 'restore' | 'prepare';
  element: HTMLElement;
  overlay: HTMLElement | null;
  removalId: RemovalId | null;
}

onTrigger는 검증과 측정 뒤, 캡처 및 DOM 커밋 전에 실행되며 이때 overlay는 null입니다. onStart는 콘텐츠 작업이 커밋되고 시각 재생이 시작된 뒤 실행됩니다. onComplete는 시각 효과와 레이아웃이 정상적으로 완료된 경우에만 실행됩니다. onError는 준비, 캡처, 애니메이션, 오디오, 정리 및 애플리케이션 콜백 오류를 받으며, 이러한 오류가 필요한 DOM 작업이나 정리를 중단하지 못하게 합니다.

GitHub에서 이 페이지 편집