> Как реализовать анимацию блока с подпрыгиванием, изменением цвета и улетом за пределы экрана (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: MTS
Стек: JavaScript
> Пример ответа
Короткий ответ
Анимация блока с подпрыгиванием, изменением цвета и улётом за экран реализуется через комбинацию CSS-переходов и keyframe-анимаций, управляемых JavaScript-состоянием. Оптимальный подход - использовать Web Animations API или CSS-анимации с animationend для последовательного запуска этапов. Ключевой момент - разделение логики на фазы: bounce → color shift → fly out, с явным управлением таймингом и завершением через requestAnimationFrame или промисы.
Подробное объяснение
Задача состоит из трёх последовательных фаз, каждая из которых требует своего подхода:
-
Подпрыгивание - циклическая анимация с затуханием амплитуды. Реализуется через keyframes с несколькими bounce-точками (например, 0%, 20%, 40%, 60%, 80%, 100%), где высота прыжка уменьшается. Можно использовать CSS
animationсease-outна каждом bounce. -
Изменение цвета - либо плавный переход через
transitionнаbackground-color, либо отдельная keyframe-анимация. Для плавности лучше использоватьtransitionс длительностью 300-500ms, но в контексте последовательности удобнее keyframes. -
Улёт за экран - трансформация
translateс большим значением (например,translateY(-200vh)илиtranslateX(100vw)), часто с вращением для эффектности. Здесь важно учитыватьwill-change: transformдля производительности.
Управление последовательностью - ключевая сложность. Варианты:
- CSS-only: через
animation-delayна каждом этапе, но это хрупко и сложно синхронизировать с состоянием. - JavaScript + события: слушать
animationendи запускать следующую фазу. Надёжно, но требует аккуратной очистки слушателей. - Web Animations API:
element.animate()возвращаетAnimationобъект с промисомfinished- это самый чистый способ для последовательных анимаций. - Промисы +
requestAnimationFrame: полный контроль, но больше кода.
Для senior-уровня важно обсудить trade-off между подходами: производительность (композитинг transform/opacity), доступность (prefers-reduced-motion), отмену анимации при размонтировании компонента.
На практике
На практике я бы выбрал Web Animations API, потому что он даёт:
- нативный промис
finishedдля композиции анимаций; - возможность отмены через
animation.cancel(); - контроль над fill mode (
forwardsдля удержания конечного состояния); - отсутствие необходимости в дополнительных библиотеках.
Альтернатива - CSS-классы с animationend, но это требует ручного управления слушателями и сложнее в отмене.
Важный нюанс: для улёта за экран использовать translate с viewport-единицами, а не top/left, чтобы не вызывать reflow. Также стоит добавить will-change: transform на время анимации и убрать после.
Пример кода
JAVASCRIPTasync function animateBlock(el) {// Фаза 1: подпрыгиваниеconst bounce = el.animate([{ transform: 'translateY(0)' },{ transform: 'translateY(-80px)', offset: 0.2 },{ transform: 'translateY(0)', offset: 0.4 },{ transform: 'translateY(-40px)', offset: 0.6 },{ transform: 'translateY(0)', offset: 0.8 },{ transform: 'translateY(-10px)', offset: 0.9 },{ transform: 'translateY(0)' }],{ duration: 800, easing: 'ease-out' });await bounce.finished;// Фаза 2: изменение цветаconst color = el.animate([{ backgroundColor: '#3498db' }, { backgroundColor: '#e74c3c' }],{ duration: 400, fill: 'forwards' });await color.finished;// Фаза 3: улёт за экранconst flyOut = el.animate([{ transform: 'translateY(0) rotate(0deg)', opacity: 1 },{ transform: 'translateY(-120vh) rotate(720deg)', opacity: 0.5 }],{ duration: 700, easing: 'cubic-bezier(0.55, 0, 0.55, 0.2)', fill: 'forwards' });await flyOut.finished;}// Использованиеconst block = document.querySelector('.block');block.addEventListener('click', () => animateBlock(block));
CSS для базового состояния:
CSS.block {width: 100px;height: 100px;background: #3498db;border-radius: 8px;will-change: transform;}
Для отмены при размонтировании:
JAVASCRIPTlet currentAnimation = null;async function animateBlock(el) {currentAnimation?.cancel();// ... та же логика, но сохраняем ссылки}
Как отвечать на собеседовании
Начни с декомпозиции задачи на три фазы и сразу предложи Web Animations API как основной инструмент. Объясни, почему не CSS-only: сложность синхронизации и отмены. Упомяни про animation.finished как промис - это показывает понимание современных API.
Затем перейди к деталям реализации: keyframes для bounce с затуханием, fill: 'forwards' для удержания цвета, использование viewport-единиц для улёта. Добавь про производительность: transform/opacity не вызывают reflow, will-change нужно убирать после завершения.
В конце упомяни edge cases: prefers-reduced-motion, отмена при размонтировании, обработка ошибок через try/catch вокруг finished.
Что проверяет интервьюер
- Понимание разницы между CSS-анимациями и Web Animations API.
- Умение композировать последовательные анимации без гонок состояний.
- Знание про производительность: compositor-friendly свойства.
- Внимание к деталям: fill mode, отмена, доступность.
- Способность предложить несколько подходов и обосновать выбор.
Типичные ошибки
- Использование
setTimeoutдля синхронизации - хрупко и не учитывает реальную длительность анимации. - Забывают про
fill: 'forwards'- блок возвращается в исходное состояние после анимации. - Анимация
top/leftвместоtransform- вызывает reflow и лаги. - Не обрабатывают отмену анимации при повторном клике или размонтировании.
- Игнорируют
prefers-reduced-motion- для пользователей с vestibular disorders это критично. - Пытаются сделать всё одной анимацией с огромным количеством keyframes - сложно поддерживать.
> Похожие задачи по frontend
Что такое дерево виджетов во Flutter
Что выбрать для анимации: CSS или JavaScript библиотеку и почему
Как добавить элемент в массив с помощью метода push в JavaScript
Как получить ключ элемента в JavaScript
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью