Vanilla Disintegratev1.6.1
RU
На этой странице

Аудио входит в состав готового пресета и не связано с пользовательским визуальным эффектом. Вызывайте remove() или restore() из пользовательского действия, например клика, чтобы библиотека могла возобновить Web Audio.

Звук пресета и пользовательский звук

Встроенный пресет воспроизводит звук по умолчанию. Его можно только заглушить: замена аудио создала бы уже другой пресет.

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 ограничивает ожидание нативного аудио перед операцией: по умолчанию 1500 мс. При превышении времени причина передаётся в onError, а анимация продолжается без звука. Можно задать другое число миллисекунд или false для неограниченного ожидания. Отмена немедленно прекращает ожидание. Общая загрузка может продолжиться для следующих операций; явный prepareAudio() по-прежнему ждёт декодирования.

У каждого экземпляра есть LRU-бюджет 8 MiB для прямых и развёрнутых декодированных буферов. Экземпляры на одной странице совместно используют загрузки, буферы и один AudioContext со счётчиком ссылок. Бюджет владения экземпляра задаётся через audioPreparation.cacheByteBudget; destroy() освобождает его владение.

Пара удаления и восстановления

const snap = '/sounds/snap.mp3';
Что требуетсяremoverestore
Один файл, развёрнутый при восстановлении{ src: snap }{ src: snap, reverse: true }
Два разных файла{ src: out }{ src: back }
Звук только при удалении{ src: snap }false
Звук только при восстановленииfalse{ src: back }

Все встроенные пресеты используют первый вариант.

Встроенные имена и URL файлов

Корневой entry point и /snapdom распознают стабильные имена src, не связывая выбор звука с геометрией частиц:

type BuiltInSound = 'dust' | 'scatter' | 'vapor' | 'wind';

const sound = { src: 'wind', volume: 0.3 };

Импортируйте /sounds, если другой аудиодвижок или независимый /core должен получить сгенерированные URL файлов:

import { builtInSounds } from 'vanilla-disintegrate/sounds';

audioElement.src = builtInSounds.vapor;

Тип builtInSounds — Readonly<Record<BuiltInSound, string>>.

Файлы остаются сжатыми и кэшируемыми ресурсами. Они не встраиваются в JavaScript как base64 и не декодируются заранее.

Поддерживаемые источники и локальные файлы

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 становится fade-in. Развёрнутая декодированная копия кэшируется; данные пользовательского 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() для обязательной очистки.

Изоляция ошибок

Ограничения autoplay, ошибки загрузки и декодирования, а также исключения пользовательского плеера сами по себе не отклоняют визуальную операцию. Они передаются в onError, а анимация и выбранное действие с DOM продолжаются.

Редактировать страницу на GitHub