오디오는 완성된 프리셋의 일부이며 사용자 시각 효과와는 독립적입니다. 라이브러리가 Web Audio 재생을 재개할 수 있도록 클릭 같은 사용자 동작에서 remove() 또는 restore()를 호출하세요.
프리셋 오디오와 사용자 오디오
내장 프리셋은 기본적으로 소리를 재생합니다. 음소거만 할 수 있으며, 오디오를 교체하면 다른 프리셋이 됩니다.
const audible = new Disintegrator({ preset: 'dust' });
const muted = new Disintegrator({ preset: 'dust', sound: false });
effects.remove(element, { preset: 'vapor', sound: false });
사용자 효과는 기본적으로 무음입니다. 오디오가 필요하면 명시적인 쌍을 전달하세요.
import Disintegrator, { createParticleEffect } from 'vanilla-disintegrate/snapdom';
const effects = new Disintegrator({
effect: createParticleEffect(),
sound: {
remove: { src: 'dust', volume: 0.35 },
restore: { src: 'dust', volume: 0.35, reverse: true },
},
});
remove와 restore는 독립적입니다. 하나 이상을 설정해야 하며, 의도적으로 무음인 단계에는 false를 사용합니다.
type SoundDefinition = SoundOptions | SoundFactory;
type SoundPair =
| { remove: SoundDefinition; restore?: SoundDefinition | false }
| { remove?: SoundDefinition | false; restore: SoundDefinition };
type SoundSelection = SoundPair;
첫 상호작용 전에 준비
활성 오디오는 기본적으로 브라우저 유휴 시간에 준비됩니다. 초기 작업량보다 즉시 준비되는 것이 중요하면 immediate를 사용하세요.
const effects = new Disintegrator({
preset: 'vapor',
audioPreparation: 'immediate',
});
sounds 필드는 시각 효과 이름이 아니라 실제 사운드 정의나 사운드 쌍을 받습니다.
type SoundPreparationSelection =
SoundDefinition | SoundSelection | readonly (false | SoundDefinition | SoundSelection)[];
import { builtInPresets } from 'vanilla-disintegrate';
const effects = new Disintegrator({
preset: 'dust',
audioPreparation: {
strategy: 'idle',
sounds: [builtInPresets.dust.sound, builtInPresets.wind.sound],
},
});
await effects.prepareAudio({
remove: { src: 'scatter' },
restore: { src: 'scatter', reverse: true },
});
effects.discardPreparedAudio();
effects.clearPreparedAudio();
audioPreparation: false는 자동 준비를 끄지만 명시적 prepareAudio()는 계속 동작합니다. 네이티브 오디오가 준비되지 않았다면 시각 효과와 사운드가 디코딩을 기다린 뒤 함께 시작합니다. 사용자 플레이어 팩토리는 자체 로딩을 관리하며 준비 중에는 호출되지 않습니다.
audioWaitTimeout은 작업이 네이티브 오디오를 기다리는 시간을 제한하며 기본값은 1500ms입니다. 시간이 초과되면 onError로 이유를 전달하고 애니메이션은 소리 없이 계속됩니다. 다른 밀리초 값이나 무제한 대기를 위한 false를 지정할 수 있습니다. 취소하면 대기가 즉시 끝납니다. 공유 로딩은 다음 작업을 위해 계속될 수 있으며 명시적 prepareAudio()는 여전히 디코딩 완료를 기다립니다.
각 인스턴스는 정방향 및 역방향 디코딩 버퍼를 위한 8 MiB LRU 예산을 가집니다. 같은 페이지의 인스턴스는 요청, 디코딩 버퍼와 참조 카운트 방식의 AudioContext 하나를 공유합니다. audioPreparation.cacheByteBudget으로 인스턴스 소유권 예산을 설정하고, destroy()로 해당 소유권을 해제합니다.
삭제와 복원 페어링
const snap = '/sounds/snap.mp3';
| 원하는 동작 | remove | restore |
|---|---|---|
| 한 파일을 복원 시 역재생 | { src: snap } | { src: snap, reverse: true } |
| 서로 다른 두 파일 | { src: out } | { src: back } |
| 삭제할 때만 재생 | { src: snap } | false |
| 복원할 때만 재생 | false | { src: back } |
모든 내장 프리셋은 첫 번째 구성을 사용합니다.
내장 이름과 애셋 URL
루트 및 /snapdom 엔트리는 오디오 선택을 파티클 지오메트리에 결합하지 않고 다음의 안정적인 src 이름을 해석합니다.
type BuiltInSound = 'dust' | 'scatter' | 'vapor' | 'wind';
const sound = { src: 'wind', volume: 0.3 };
다른 오디오 시스템이나 의존성이 없는 /core 엔트리에서 생성된 파일 URL이 필요하면 /sounds를 가져오세요.
import { builtInSounds } from 'vanilla-disintegrate/sounds';
audioElement.src = builtInSounds.vapor;
builtInSounds의 타입은 Readonly<Record<BuiltInSound, string>>입니다.
파일은 인코딩되고 캐시 가능한 애셋으로 유지됩니다. base64로 JavaScript에 포함하거나 미리 디코딩하지 않습니다.
지원 소스와 로컬 파일
type SoundSource = BuiltInSound | string | URL | Blob | ArrayBuffer | ArrayBufferView | AudioBuffer;
문자열과 URL은 가져오고, blob과 인코딩된 바이트는 로컬에서 디코딩하며, 준비된 AudioBuffer는 직접 재사용합니다. 브라우저 File은 Blob이므로 사용자 오디오는 기기에만 남을 수 있습니다.
import Disintegrator, { createParticleEffect } from 'vanilla-disintegrate/snapdom';
const file = fileInput.files?.[0];
if (file) {
const effects = new Disintegrator({
effect: createParticleEffect(),
sound: { remove: { src: file }, restore: false },
});
}
패키지는 파일을 업로드하거나 영구 저장하지 않습니다.
재생 옵션
interface SoundOptions {
src: SoundSource;
reverse?: boolean;
volume?: number;
duration?: number;
fadeDuration?: number;
delay?: number;
playbackRate?: number;
}
const sound = {
src: new URL('/sounds/snap.ogg', location.href),
volume: 0.35,
delay: 40,
duration: 0.7,
fadeDuration: 0.12,
playbackRate: 1.05,
};
reverse의 기본값은 false이며 선형 volume은 0에서 1 사이이고 기본값은 1입니다. delay는 밀리초 단위이며 기본값은 0입니다. duration과 fadeDuration은 초 단위입니다. 재생 시간을 생략하면 원본 길이를 넘지 않는 범위에서 시각 효과를 따르고 페이드는 기본적으로 0입니다. playbackRate 기본값은 1이며 최소 0.01로 정규화됩니다.
역재생
const remove = { src: '/sounds/snap.mp3' };
const restore = { src: '/sounds/snap.mp3', reverse: true };
reverse를 사용하면 역재생 녹음의 끝이 시각 효과 끝에 도달하도록 정렬됩니다. 소스 끝에서 잘라내고 fadeDuration은 페이드인이 됩니다. 역방향 디코딩 복사본은 캐시되며 호출자가 제공한 AudioBuffer 데이터는 변경하지 않습니다.
사용자 플레이어
팩토리로 기존 오디오 엔진을 연결할 수 있습니다.
interface SoundContext {
operation: 'remove' | 'restore';
element: HTMLElement;
signal: AbortSignal;
}
interface SoundPlayback {
finished?: PromiseLike<unknown>;
stop?(): void;
dispose?(): void;
}
type SoundFactory = (context: SoundContext) => SoundPlayback | PromiseLike<SoundPlayback | void> | void;
const sound = ({ operation, element, signal }) => {
const playback = audioEngine.play(operation === 'remove' ? 'card-out' : 'card-in');
const stop = () => playback.stop();
signal.addEventListener('abort', stop, { once: true });
return {
finished: playback.finished,
stop,
dispose() {
signal.removeEventListener('abort', stop);
playback.dispose();
},
};
};
팩토리는 작업, 대상 요소와 중단 신호를 받습니다. finished는 사용자 재생이 끝난 뒤 조기 정리를 요청할 수 있습니다. 취소에는 stop(), 항상 실행할 정리에는 dispose()를 반환하세요.
오류 격리
자동 재생 제한, 요청 실패, 디코딩 오류와 사용자 플레이어 예외는 오디오만을 이유로 시각 작업을 거부하지 않습니다. 오류는 onError로 전달되고 애니메이션과 선택한 DOM 동작은 계속됩니다.