Vanilla Disintegrate — современные и эффектные анимации для веба
- суббота, 5 сентября 2026 г. в 00:00:12

Какие обычно бывают анимации в вебе? Появление, исчезновение, небольшой сдвиг, масштабирование. Они не обязательно плохие: чаще всего интерфейсу действительно не нужно ничего сложнее. Иногда лучшее решение — вообще не добавлять анимацию. Так интерфейс остаётся быстрым, понятным и не отвлекает от основной задачи. Но иногда хочется сделать один небольшой момент запоминающимся. Не длинную заставку и не тяжёлый спецэффект на весь экран, а короткую реакцию на действие пользователя. Меньше секунды — и удаление карточки, появление элемента или отмена действия уже ощущаются совсем иначе.
Однажды я как раз занимался очередным удалением элемента из «Избранного». Задача знакомая: пользователь нажимает на закладку, карточка исчезает, соседние элементы плавно сдвигаются. Можно добавить opacity, можно чуть изменить transform, но всё это уже настолько привычно, что почти не замечается. Хотелось не просто сделать элемент прозрачным или увести его в сторону, а найти более интересный, но ненавязчивый способ удаления. Пример такой анимации я увидел в мессенджере при удалении сообщений. Позже узнал, что её часто называют «щелчком Таноса».
Казалось, что готовое решение наверняка уже существует. Похожие варианты нашлись, но ни один не давал того, что мне было нужно. Мне хотелось найти не заготовку для одного эффекта, а что-то, что можно использовать с обычным DOM, без привязки к фреймворку и с возможностью настроить анимацию под свой интерфейс. Тогда я решил сделать свой вариант для рабочей задачи.
Когда я создавал свой первый open-source проект, у меня впервые появилась мысль: а вдруг это будет полезно ещё кому-то? С тех пор я каждый раз спрашиваю себя, можно ли превратить рабочее решение во что-то, чем смогут пользоваться другие разработчики. Мне за это никто не платит, и я до сих пор не могу до конца объяснить, зачем трачу на это столько времени. Наверное, мне просто нравится мысль, что написанным мной кодом будут пользоваться люди из разных уголков мира — от разработчика, который только начинает, до человека с большим опытом, — и что кто-то из них заметит и оценит мою работу. И, наверное, мне хочется оставить после себя в цифровом мире что-то полезное.
Так из этой рабочей задачи и выросла ещё одна open-source библиотека — Vanilla Disintegrate. Она анимирует удаление и восстановление DOM-элементов с помощью частиц. В ней есть готовые эффекты, а если хочется свой, не нужно вручную перебирать параметры в коде: на сайте есть интерактивный плейграунд. В нём можно настроить движение частиц, время и звук, посмотреть результат и сразу скопировать готовую конфигурацию. А если этого недостаточно, можно вообще заменить способ отрисовки.
Ниже расскажу, как она работает, почему за обычным «удалить элемент красиво» скрывается чуть больше, чем несколько CSS-анимаций, и где у такого подхода есть реальные ограничения.
В простом случае библиотеку можно подключить и использовать вот так:
import Disintegrator from 'vanilla-disintegrate/snapdom'; const effects = new Disintegrator({ preset: 'dust' }); const card = document.querySelector<HTMLElement>('.card'); document.querySelector('#remove')?.addEventListener('click', () => { if (card) effects.remove(card); });
После клика карточка рассыплется, а исходный элемент будет удалён из DOM. Никакой специальной разметки, обязательного CSS-файла или компонента под конкретный фреймворк здесь нет. На первый взгляд кажется, что библиотека просто добавляет красивую анимацию поверх элемента. Но если бы всё было настолько просто, можно было бы обойтись несколькими CSS-свойствами.
CSS отлично подходит для многих задач. Плавно показать модальное окно, подсветить кнопку, сдвинуть список, сделать красивый hover — всё это его работа. Но CSS анимирует сам DOM-элемент целиком. Он не умеет взять уже отрисованную карточку вместе с текстом, изображением, градиентом и тенью, разделить её на сотни маленьких кусочков и дать каждому свою траекторию. Можно было бы создать много маленьких <div> и двигать их по отдельности. Но представьте, что происходит с производительностью, когда таких элементов становится сотни или тысячи. Да и как перенести в эти div реальный текст, картинку, тени и все детали исходной карточки?
Поэтому библиотека сначала превращает элемент в изображение, а уже с ним работает как с материалом для частиц:
обычный DOM-элемент ↓ Canvas-снимок элемента ↓ частицы, которые рисуются через WebGL2 ↓ временный слой над страницей
Пока пользователь видит частицы, исходный DOM-элемент уже может исчезнуть из списка. Визуально переход остаётся цельным: на его месте ещё есть изображение карточки, но теперь оно постепенно превращается в пыль.
Получить снимок обычного DOM-элемента оказалось гораздо интереснее. Браузер сам отлично рисует страницу, но не даёт стабильного универсального API: «возьми вот этот элемент и верни мне ровно те пиксели, которые пользователь сейчас видит». Поэтому внешний вид элемента приходится воспроизводить отдельно в Canvas.
Для готового варианта библиотеки я использую SnapDOM. Он получает элемент, переносит его содержимое и стили в промежуточное представление и возвращает Canvas. Этого Canvas уже достаточно, чтобы сделать эффект частиц. Но SnapDOM — не часть ядра и не единственный возможный вариант. Библиотека принимает свой capture-адаптер: можно использовать другой движок, заранее подготовленный Canvas или написать собственную реализацию. Это важно, потому что у разных проектов разные требования к скорости, качеству и типам контента.
И здесь стоит сказать честно: снимок DOM не всегда будет попиксельно совпадать с оригиналом. Особенно это бывает заметно на одиночных буквах, тонких линиях, сложных фильтрах и при нестандартном масштабе страницы. Для карточек, изображений и законченных блоков отличия обычно почти не видны, но обещать идеальную копию в каждом браузере было бы неправильно.
WebGL2 — это браузерный API для отрисовки графики на GPU. Его часто используют в 3D-сценах, визуализациях и играх, но он не стал обычным способом анимировать интерфейс. Для кнопки, модального окна или плавного сдвига списка проще и правильнее использовать CSS или Web Animations API: они работают прямо с DOM и не требуют вручную создавать текстуры, писать шейдеры и следить за ресурсами видеокарты.
Для нужного эффекта ситуация другая. Нужно одновременно двигать много маленьких фрагментов одного изображения, поэтому GPU подходит гораздо лучше сотен DOM-узлов. Именно в этом месте WebGL2 оправдывает свою сложность.
Когда Canvas-снимок готов, библиотека передаёт его встроенному WebGL2-рендереру. Он загружает изображение в текстуру и делит непрозрачные области на небольшие блоки. Каждый блок становится будущей частицей. У частицы есть стартовая позиция, момент появления, направление, скорость, поворот и прозрачность. Благодаря этому эффект можно сделать совершенно разным: направленным, хаотичным, спокойным, похожим на пыль или на вихрь.
Есть четыре готовых пресета:
const dust = new Disintegrator({ preset: 'dust' }); const scatter = new Disintegrator({ preset: 'scatter' }); const vapor = new Disintegrator({ preset: 'vapor' }); const wind = new Disintegrator({ preset: 'wind' });
Но интереснее не просто выбрать один из них. Удаление и восстановление — это две независимые фазы. Например, карточка может разлетаться влево, а возвращаться сверху с лёгким вихрем:
import Disintegrator, { createParticleEffect } from 'vanilla-disintegrate/snapdom'; const effect = createParticleEffect({ remove: { curve: 'burst', release: 'left', duration: 700, }, restore: { curve: 'float', release: 'top', duration: 900, swirl: 5, }, }); const effects = new Disintegrator({ effect });
У этого примера нет цели показать все параметры библиотеки. Их довольно много, и запоминать их не нужно.
Когда я начал делать библиотеку, я довольно быстро понял, что одной анимации удаления недостаточно. В интерфейсах почти всегда есть «Отменить»: пользователь убрал карточку из избранного, передумал и хочет вернуть её обратно. Можно, конечно, заново создать элемент. Но иногда удобнее сохранить исходный DOM-узел, а потом вернуть его туда, куда нужно приложению:
const operation = effects.remove(card, { retain: true }); // Пользователь нажал «Отменить». await operation.finished; const retainedCard = effects.take(operation.removalId); if (retainedCard) { document.querySelector('.favorites')?.append(retainedCard); effects.restore(retainedCard); }
Карточка может вернуться даже не на прежнее место, а в другой список или в уже изменившийся layout. Приложение управляет DOM как обычно, а библиотека просто красиво показывает появление элемента там, где он оказался.
Частицы — это то, ради чего всё началось, но я не хотел делать библиотеку, которая умеет ровно один трюк. Каждый эффект в библиотеке задаётся двумя независимыми фазами: удаления и восстановления. Для каждой можно использовать Web Animations API, CSS, Canvas 2D, SVG, другую WebGL-библиотеку или собственный рендерер. Если эффекту не нужен Canvas-снимок, его вообще можно отключить для этой фазы.
Это означает, что Vanilla Disintegrate может быть не только про «щелчок Таноса». В одном месте интерфейса можно использовать эффект частиц, в другом — спокойное исчезновение, а в третьем — собственную анимацию. При этом остаются общими жизненный цикл DOM-элемента, отмена операции, звук и уборка временных ресурсов.
Вопрос закономерный. Эффект выглядит заметно сложнее обычного opacity, значит и ресурсов потребляет больше. Здесь есть две разные части работы. Сначала нужно сделать снимок элемента — это кратковременная задача для процессора. Потом частицы рисуются на GPU через WebGL2.
На странице может быть сколько угодно карточек с таким эффектом, но это не значит, что все они будут анимироваться одновременно. Обычно анимация запускается после конкретного действия пользователя: одна операция заканчивается, её временные ресурсы освобождаются, затем запускается следующая. В таком сценарии даже большое количество элементов на странице само по себе не создаёт заметной нагрузки.
Но если попытаться одновременно распылить несколько огромных блоков на весь экран, да ещё на устройстве с высокой плотностью пикселей, ситуация будет другой. Поэтому в библиотеке есть режим качества auto: по умолчанию он ограничивает рабочее разрешение и не даёт одному эффекту неожиданно занять слишком много памяти.
Для небольших элементов, где особенно важна детализация, можно выбрать exact:
import { createParticleEffect } from 'vanilla-disintegrate/snapdom'; const effect = createParticleEffect({ remove: { renderQuality: 'exact' }, restore: { renderQuality: 'exact' }, });
Но exact — не волшебная кнопка. Он сохраняет полное разрешение уже сделанного снимка, а не делает сам снимок точнее. Если захват DOM немного отличается от оригинала, повышенное разрешение это не исправит.
Ещё одна важная вещь — WebGL-контексты. Браузер не рассчитан на то, чтобы страница бесконечно создавала новые контексты для каждой карточки. Поэтому после завершения эффекта временные текстуры удаляются, а контексты переиспользуются через ограниченный общий пул.
WebGL2 нужен только для встроенных эффектов частиц. Если браузер или устройство не смогли создать контекст, пользовательское действие всё равно не должно ломаться: элемент удалится или восстановится, а визуальная часть завершится со статусом skipped.
Но это не значит, что пользователь обязательно увидит мгновенное исчезновение. Для любого эффекта, который требует WebGL2, можно передать резервную анимацию на CSS, Web Animations API, Canvas 2D, SVG или собственном рендерере. Она запустится, если WebGL2 недоступен или встроенный рендерер частиц не смог начать работу. Так на современном устройстве будут частицы, а в остальных случаях — аккуратное обычное исчезновение.
Пользовательские эффекты через CSS, Canvas 2D или Web Animations API от WebGL2 не зависят. Библиотека также учитывает prefers-reduced-motion, поэтому при включённом системном ограничении лишняя анимация не будет запускаться.
Vanilla Disintegrate рассчитана на современные браузеры. Полную таблицу поддерживаемых версий, ограничения захвата и особенности Safari я вынес в документацию, чтобы не превращать эту статью в справочник.
Я не думаю, что теперь каждый крестик в интерфейсе должен сопровождаться облаком частиц. Анимация ради анимации быстро надоедает и начинает мешать. Но есть действия, где она уместна: удаление важной карточки, очистка списка, отмена действия, завершение задачи, перемещение объекта в корзину. В этих местах короткая визуальная реакция помогает заметить результат действия и просто делает интерфейс чуть живее. Именно для таких случаев я и сделал Vanilla Disintegrate. Библиотека не пытается заменить обычные CSS-анимации. Она нужна тогда, когда хочется добавить один заметный, но управляемый эффект — без привязки к фреймворку и без необходимости строить свой WebGL-движок с нуля.
Vanilla Disintegrate распространяется по лицензии MIT и бесплатна для любых проектов. Я буду рад, если она окажется кому-то полезной: будь то звезда на GitHub, issue с реальным сценарием, идея для нового эффекта или просто ссылка на проект, в котором она пригодилась.
Документация и плейграунд: disintegrate.uvarov.tech
Посмотреть эффект в реальном использовании: Vanilla Calendar Pro