Анимированный баннер — это рекламный или промо-блок фиксированного размера, движение в котором задано кодом, а не заранее отрисованным видео. Сегодня такие баннеры собирают из HTML, CSS и JavaScript вместо устаревшего Flash. В статье разберу три способа анимации (CSS, DOM плюс JavaScript, canvas), дам минимальный рабочий пример на canvas и requestAnimationFrame, покажу, почему он лучше setInterval, и как удержать баннер легким и плавным.
Содержание
- Три подхода к анимации баннера
- Когда хватает чистого CSS
- Рабочий пример на canvas и requestAnimationFrame
- Почему requestAnimationFrame, а не setInterval
- Библиотека GSAP, когда движения много
- Оптимизация: не блокировать поток и рисовать на GPU
- Адаптивность и четкость на разных экранах
- Вес баннера: почему это важно
- Выводы
- Где применяется / связь с практикой
- FAQ
Три подхода к анимации баннера
Выбор техники зависит от сложности движения. Ниже — ориентир, что брать под задачу.
| Подход | Когда подходит | Плюсы | Ограничения |
|---|---|---|---|
| CSS-анимации и transitions | простое движение готовых элементов (сдвиг, затухание, масштаб) | не нужен JavaScript, браузер сам считает кадры на GPU | нет сложной логики, тяжело управлять по ходу |
| DOM-элементы плюс JavaScript | нужна логика, реакция на клик, смена сцен | полный контроль, легко читать разметку | много узлов тормозят перерисовку |
Canvas 2D плюс requestAnimationFrame |
частицы, множество объектов, попиксельная отрисовка | одна поверхность вместо сотен узлов, высокая производительность | все рисуем руками, нет готовых DOM-событий на фигурах |
Общее правило: если движение раскладывается на пару CSS-свойств — берите CSS. Если объектов десятки и они пересчитываются каждый кадр — берите canvas. DOM плюс JavaScript — это середина для интерактивных сцен из нескольких блоков.
Когда хватает чистого CSS
Самый легкий баннер вообще не требует JavaScript. Движение задается через @keyframes и свойство transform, а браузер сам синхронизирует кадры с частотой экрана.
<div class="banner">
<div class="box"></div>
</div>
.banner {
position: relative;
width: 300px;
height: 250px;
overflow: hidden;
background: #f1f3f4;
}
.box {
position: absolute;
top: 100px;
width: 50px;
height: 50px;
background: #1a73e8;
animation: slide 2s linear infinite;
}
@keyframes slide {
from { transform: translateX(0); }
to { transform: translateX(250px); }
}
Результат: в браузере синий квадрат едет слева направо по серому баннеру 300×250 и зацикливается каждые 2 секунды. JavaScript тут не нужен вовсе. Важно, что анимируется transform, а не свойство left — об этой разнице отдельный раздел ниже.
Разница между transition и animation: transition проигрывает переход между двумя состояниями по событию (наведение, смена класса), а animation с @keyframes крутит заданную последовательность сама, в том числе бесконечно. Для баннера, который анимируется без действий пользователя, нужен именно animation.
Рабочий пример на canvas и requestAnimationFrame
Когда объектов много или нужна попиксельная отрисовка, DOM становится дорогим: каждый узел браузер размещает и перерисовывает. Canvas — это одна поверхность, на которой мы сами рисуем каждый кадр. Вот минимальный полный баннер, который можно вставить в пустую HTML-страницу и открыть в браузере.
<canvas id="banner" width="300" height="250"></canvas>
const canvas = document.getElementById('banner');
const ctx = canvas.getContext('2d');
let x = 0;
function frame() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // стираем прошлый кадр
ctx.fillStyle = '#1a73e8';
ctx.fillRect(x, 100, 50, 50); // рисуем квадрат
x += 2; // сдвигаем на 2 px
if (x > canvas.width) x = -50; // ушел за край - вернули влево
requestAnimationFrame(frame); // просим следующий кадр
}
requestAnimationFrame(frame);
Результат: в браузере синий квадрат едет слева направо и, уйдя за правый край, появляется снова слева. Разберу по шагам:
getContext('2d')дает объект для рисования 2D-фигур на canvas.clearRectстирает предыдущий кадр целиком — без этого квадрат оставлял бы шлейф.fillRect(x, 100, 50, 50)рисует квадрат 50×50 в текущей позицииx.x += 2меняет состояние, аrequestAnimationFrame(frame)планирует следующий вызов перед очередной перерисовкой экрана.
У этого примера есть скрытая граница: шаг x += 2 привязан к кадру, а не ко времени. На экране 60 Гц квадрат едет со скоростью 120 px/с, а на 144 Гц — вдвое быстрее. Чтобы скорость была одинаковой на любом мониторе, считайте сдвиг от прошедшего времени.
const canvas = document.getElementById('banner');
const ctx = canvas.getContext('2d');
const speed = 120; // пикселей в секунду
let x = 0;
let last = 0;
function frame(now) {
const dt = last ? (now - last) / 1000 : 0; // секунд с прошлого кадра
last = now;
x += speed * dt;
if (x > canvas.width) x = -50;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#1a73e8';
ctx.fillRect(x, 100, 50, 50);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
Результат: тот же едущий квадрат, но теперь его скорость — ровно 120 пикселей в секунду и не зависит от частоты экрана. Аргумент now в колбэк передает сам requestAnimationFrame — это метка времени текущего кадра в миллисекундах.
Почему requestAnimationFrame, а не setInterval
Раньше анимацию крутили через setInterval(frame, 16). Так делать не стоит по нескольким причинам.
setIntervalне знает про частоту экрана и про перерисовку. Он может вызвать колбэк между кадрами — и часть отрисовок пропадет впустую или даст рывки.requestAnimationFrameсинхронизирован с циклом отрисовки браузера: колбэк вызывается один раз перед каждым обновлением экрана, обычно 60 раз в секунду.- Когда вкладка неактивна, браузер приостанавливает
requestAnimationFrame— баннер не жжет процессор и батарею в фоне.setIntervalпродолжает срабатывать вслепую.
Вот тот же баннер на setInterval — как антипример, который дает рывки:
let x = 0;
setInterval(() => { // фиксированные 16 мс, не привязаны к кадру
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(x, 100, 50, 50);
x += 2;
if (x > canvas.width) x = -50;
}, 16);
Результат: квадрат тоже едет, но при нагрузке на страницу движение дергается, а в фоновой вкладке таймер продолжает работать вхолостую. Для новой анимации используйте requestAnimationFrame; setInterval уместен разве что для логики, не связанной с отрисовкой.
Библиотека GSAP, когда движения много
Писать вручную сложные тайминги (несколько объектов, задержки, плавности) утомительно. Для этого есть GSAP (GreenSock Animation Platform) — популярная библиотека анимации, которая работает и с DOM-элементами, и с canvas, и с SVG. Она берет на себя расчет промежуточных кадров и последовательностей (timeline).
// подключив gsap на страницу
gsap.to('.box', { x: 250, duration: 2, repeat: -1, ease: 'power1.inOut' });
Результат: элемент с классом box едет на 250 пикселей вправо за 2 секунды, бесконечно повторяя движение с плавным ускорением и замедлением. GSAP оправдан на сложных сценах; для простого сдвига квадрата хватит CSS или canvas без внешних зависимостей — лишняя библиотека утяжеляет баннер.
Оптимизация: не блокировать поток и рисовать на GPU
Плавность баннера — это стабильные кадры без пропусков. Два главных приема.
Первое: не блокировать основной поток. JavaScript в браузере однопоточный — тяжелый расчет в колбэке кадра задержит отрисовку и даст рывки. Внутри frame() держите только минимум: не создавайте новые объекты и массивы каждый кадр (это нагружает сборщик мусора), не читайте размеры DOM в цикле, тяжелые вычисления выносите заранее или в Web Worker.
Второе: анимировать свойства, которые считаются на видеокарте. Браузер рисует страницу слоями, и часть свойств композитор двигает на GPU без пересчета геометрии. Практическое правило для DOM-анимаций:
| Свойство | Что вызывает | Для анимации |
|---|---|---|
transform (translate, scale, rotate) |
только композицию слоя | подходит, плавно |
opacity |
только композицию слоя | подходит, плавно |
left, top, width, height |
пересчет разметки (reflow) и перерисовку | избегать в анимации |
box-shadow, background |
перерисовку (repaint) | использовать осторожно |
Поэтому в CSS-примере выше двигали transform: translateX(...), а не left. Подсказка will-change: transform заранее сообщает браузеру, что элемент будет анимироваться, и помогает вынести его на отдельный слой — но применять ее стоит точечно, лишние слои тоже стоят памяти.
Для canvas правило другое: там нет CSS-слоев, зато важно минимизировать площадь clearRect и число вызовов рисования за кадр, а статичный фон выносить на отдельный, редко перерисовываемый canvas.
Адаптивность и четкость на разных экранах
Баннер часто показывают на дисплеях разной плотности. Если задать canvas жесткие пиксели, на экране с высоким DPI (Retina) картинка будет мыльной. Решение — масштабировать буфер canvas на devicePixelRatio, а видимый размер задавать через CSS.
function setupCanvas(canvas) {
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect(); // размер в CSS-пикселях
canvas.width = rect.width * dpr; // буфер под плотность экрана
canvas.height = rect.height * dpr;
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr); // рисуем в CSS-координатах
return ctx;
}
Результат: на обычном мониторе картинка та же, а на Retina квадрат и текст остаются четкими, а не размытыми. Сам видимый размер баннера задавайте в CSS процентами или через max-width: 100%, чтобы он вписывался в контейнер на телефоне. Для DOM-баннера адаптивность проще — относительные единицы и медиазапросы. При смене размера окна пересчитывайте буфер по событию resize (желательно с задержкой-throttle, чтобы не дергать пересчет на каждый пиксель).
Вес баннера: почему это важно
Рекламные площадки ограничивают размер файла баннера, и тяжелый блок либо отклонят, либо он будет грузиться дольше самой страницы. Практические ориентиры на 2026 год:
- Держите вес готового баннера небольшим — десятки, а не сотни килобайт. Точный лимит задает площадка, у Google Ads для HTML5-баннера действует ограничение около 150 КБ (значение уточняйте в справке площадки, оно менялось).
- Не тащите крупные библиотеки ради одного эффекта — CSS или чистый canvas часто закрывают задачу без зависимостей.
- Изображения сжимайте и берите современные форматы (WebP, AVIF), простую графику рисуйте кодом или через SVG, а не растровыми PNG.
- Минифицируйте CSS и JavaScript при сборке готового баннера.
Легкий баннер быстрее показывается, лучше проходит модерацию сетей и меньше нагружает устройство пользователя — это напрямую влияет на то, увидит ли человек анимацию до того, как уйдет со страницы.
Выводы
- Современный анимированный баннер собирают из HTML, CSS и JavaScript; Flash устарел и в рекламных сетях не используется.
- Для простого движения хватает CSS с
transformи@keyframes, для сложных сцен из многих объектов — canvas 2D. - Анимацию на canvas ведите через
requestAnimationFrame, а сдвиг считайте от времени (delta), чтобы скорость не зависела от частоты экрана;setIntervalдля отрисовки не подходит. - Плавность дают два правила: не блокировать основной поток и анимировать
transform/opacity, которые считаются на GPU. - Баннер держите легким и адаптивным:
devicePixelRatioдля четкости, сжатые ассеты и минимум зависимостей ради лимита площадки.
Где применяется / связь с практикой
Анимированные баннеры — частая задача фронтенд-разработчика и верстальщика: промо-блоки на сайте, рекламные креативы для сетей, интерактивные элементы лендингов. Все приемы из статьи (canvas, requestAnimationFrame, работа с transform) — это база JavaScript и работы с DOM, а не отдельная узкая тема. Чтобы уверенно писать такие вещи с нуля, нужно разобраться с типами, функциями, событиями и API браузера по порядку.
Освойте тему на практике
Системно освоить язык и довести до практики можно на курсе JavaScript Basic: там разбирают синтаксис, работу с DOM и анимацию на реальных задачах. Посмотреть формат и уровень преподавания перед стартом помогают бесплатные открытые уроки Otus — это разовые занятия по конкретным темам без оплаты.
FAQ
Можно ли сделать анимированный баннер вообще без JavaScript?
Да, для простого движения (сдвиг, затухание, поворот, масштаб) достаточно CSS с @keyframes и transform. JavaScript нужен, когда требуется логика, реакция на действия пользователя или отрисовка множества объектов на canvas.
Чем canvas отличается от SVG для баннера?
SVG — это векторные узлы в DOM: каждую фигуру видит браузер, ей можно повесить событие, она масштабируется без потери качества. Canvas — одна растровая поверхность, где мы рисуем каждый кадр сами; он быстрее при большом числе объектов, но событий на отдельных фигурах там нет. Для нескольких иконок берите SVG, для частиц и сложной динамики — canvas.
Почему баннер дергается в фоновой вкладке или на слабом телефоне?
Скорее всего, отрисовка привязана к setInterval или в кадре идет тяжелый расчет, блокирующий поток. Переведите анимацию на requestAnimationFrame, считайте движение от времени, уберите создание объектов внутри кадра и анимируйте transform/opacity вместо left/top.



