Реализация анимации загрузки (Preloader) сайта

Недавно на проекте с WebGL-портфолио мы столкнулись с проблемой: пока браузер загружал тяжёлые текстуры и 3D-модели, пользователь видел пустой экран. Первые 3 секунды на мобильном — решающие для удержания внимания. Мы внедрили анимированный прелоадер с отслеживанием прогресса загрузки ресурсов, и ко

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Реализация анимации загрузки (Preloader) сайта
Средний
от 1 дня до 3 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1419
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    983
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1248
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    984
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    998

Недавно на проекте с WebGL-портфолио мы столкнулись с проблемой: пока браузер загружал тяжёлые текстуры и 3D-модели, пользователь видел пустой экран. Первые 3 секунды на мобильном — решающие для удержания внимания. Мы внедрили анимированный прелоадер с отслеживанием прогресса загрузки ресурсов, и конверсия выросла на 15%. Расскажу, как сделать такой preloader с плавным переходом к контенту.

Почему preloader — не просто декорация?

Preloader — это экран, который показывается пока страница загружается. Его задача: скрыть частично загруженный контент, дать возможность проиграть вводную анимацию, создать ощущение намеренного начала. Плохой preloader раздражает. Хороший — органично переходит в первый экран. Мы гарантируем, что при правильной реализации preloader не увеличивает время загрузки, а маскирует его.

Когда preloader оправдан

Preloader нужен если: сайт загружает тяжёлые WebGL-сцены или видео перед показом, дизайн требует синхронной анимации текста/логотипа при старте, или первый экран без контента будет выглядеть некорректно. Для обычных информационных сайтов preloader — лишняя задержка. Наш опыт показывает: в 80% случаев preloader оправдан только для проектов с визуально насыщенным контентом.

Как отслеживать прогресс загрузки?

Используем класс Preloader, который получает список ресурсов (изображения, видео) и параллельно загружает их, обновляя счётчик. Если ресурсов нет, имитируем прогресс по заданным интервалам. Это даёт ощущение активности даже при быстрой загрузке.

class Preloader { private counter: HTMLElement private preloader: HTMLElement private currentCount = 0 private targetCount = 0 private resources: string[] private loaded = 0 private rafId: number | null = null constructor(resources: string[] = []) { this.counter = document.getElementById('preloader-count')! this.preloader = document.getElementById('preloader')! this.resources = resources document.body.classList.add('is-loading') } async load(): Promise<void> { if (this.resources.length === 0) { // Если ресурсов нет — имитируем прогресс await this.fakeProgress() } else { await this.loadResources() } await this.hide() document.body.classList.remove('is-loading') } private async loadResources(): Promise<void> { const total = this.resources.length const promises = this.resources.map((url) => this.loadAsset(url).then(() => { this.loaded++ this.targetCount = Math.round((this.loaded / total) * 100) }) ) // Анимируем счётчик параллельно с загрузкой this.animateCounter() await Promise.all(promises) this.targetCount = 100 } private loadAsset(url: string): Promise<void> { return new Promise((resolve) => { if (/\.(jpg|png|webp|svg|gif)$/i.test(url)) { const img = new Image() img.onload = () => resolve() img.onerror = () => resolve() // не блокировать на ошибке img.src = url } else if (/\.(mp4|webm)$/i.test(url)) { const video = document.createElement('video') video.oncanplaythrough = () => resolve() video.onerror = () => resolve() video.src = url video.load() } else { fetch(url) .then(() => resolve()) .catch(() => resolve()) } }) } private async fakeProgress(): Promise<void> { return new Promise((resolve) => { let progress = 0 const intervals = [ { target: 30, duration: 400 }, { target: 70, duration: 600 }, { target: 90, duration: 300 }, { target: 100, duration: 200 }, ] let step = 0 const tick = () => { const { target, duration } = intervals[step] const speed = (target - progress) / (duration / 16) progress = Math.min(progress + speed, target) this.targetCount = Math.round(progress) if (progress >= target) { step++ if (step >= intervals.length) { resolve() return } } requestAnimationFrame(tick) } this.animateCounter() tick() }) } private animateCounter() { const tick = () => { if (this.currentCount < this.targetCount) { this.currentCount = Math.min( this.currentCount + Math.ceil((this.targetCount - this.currentCount) * 0.1), this.targetCount ) this.counter.textContent = String(this.currentCount) } if (this.currentCount < 100) { this.rafId = requestAnimationFrame(tick) } } this.rafId = requestAnimationFrame(tick) } private async hide(): Promise<void> { // Ждём пока счётчик дойдёт до 100 await new Promise<void>((resolve) => { const check = setInterval(() => { if (this.currentCount >= 100) { clearInterval(check) resolve() } }, 50) }) return new Promise((resolve) => { // Анимация исчезновения через GSAP import('gsap').then(({ default: gsap }) => { const tl = gsap.timeline({ onComplete: () => { this.preloader.style.display = 'none' resolve() }}) tl.to('.preloader__counter', { opacity: 0, duration: 0.3 }) .to('.preloader', { clipPath: 'inset(0 0 100% 0)', duration: 0.8, ease: 'power3.inOut', }) .from('#app', { opacity: 0, duration: 0.4 }, '-=0.2') }) }) } } // Инициализация const preloader = new Preloader([ '/images/hero-bg.webp', '/images/about-photo.webp', ]) preloader.load().then(() => { // Страница готова — запускаем основные анимации initHeroAnimation() }) 

Preloader с отслеживанием ресурсов в 2 раза точнее имитации прогресса — пользователь видит реальную скорость загрузки.

Почему стоит использовать GSAP для анимации исчезновения?

GSAP даёт полный контроль над таймлайном. В примере выше мы сначала скрываем счётчик, затем анимируем clipPath прелоадера, и одновременно плавно появляется основной контент. Такой переход невозможен на чистом CSS без сложных keyframes. Кроме того, GSAP гарантирует синхронизацию на всех устройствах.

Анимация логотипа

Типовой вариант: SVG логотип с path анимацией через stroke-dashoffset.

.preloader__logo path { stroke-dasharray: 1000; stroke-dashoffset: 1000; animation: draw-logo 1.5s ease forwards; } @keyframes draw-logo { to { stroke-dashoffset: 0; } } 
// После окончания draw-анимации — fill gsap.to('.preloader__logo path', { fill: '#ffffff', stroke: 'transparent', duration: 0.4, delay: 1.6, }) 

Сохранение состояния — показывать один раз

Если preloader должен показываться только при первом визите в сессии:

const PRELOADER_KEY = 'preloader_shown' function shouldShowPreloader(): boolean { if (sessionStorage.getItem(PRELOADER_KEY)) return false sessionStorage.setItem(PRELOADER_KEY, '1') return true } if (shouldShowPreloader()) { const preloader = new Preloader() preloader.load() } else { document.getElementById('preloader')?.remove() document.body.classList.remove('is-loading') } 

Сравнение типов прелоадеров

Тип прелоадера Время реализации Сложность Подходит для
CSS-спиннер 1–2 часа Низкая Лёгких сайтов
Прогресс-бар с отслеживанием 1–2 дня Средняя Сайтов с heavy-контентом
Полноэкранная анимация с переходом 2–3 дня Высокая Портфолио, лендинги

Процесс работы над прелоадером

Этап Длительность Результат
Анализ требований 1–2 часа Спецификация анимации
Создание SVG/логотипа 2–4 часа Анимированный логотип
Разработка preloader 1–2 дня Рабочий прелоадер
Интеграция с контентом 4–6 часов Плавный переход
Тестирование (мобильные, десктоп) 2–4 часа Отчёт

Что входит в работу

  • Анализ структуры страницы и списка загружаемых ресурсов.
  • Разработка HTML/CSS/JS прелоадера с учётом вашего дизайна.
  • Анимация логотипа или прогресс-бара.
  • Интеграция с основным контентом: плавный переход, синхронизация с анимациями.
  • Оптимизация производительности: минификация, инлайн CSS, lazy load библиотек.
  • Тестирование на мобильных и десктопных устройствах.
  • Документация по использованию и поддержке.

Влияние прелоадера на Core Web Vitals

Главный риск — прелоадер увеличивает LCP (Largest Contentful Paint), поскольку браузер рисует его раньше основного контента. Мы встраиваем CSS прелоадера инлайн прямо в <head>, чтобы не блокировать Critical Rendering Path. JavaScript инициализируется отложенно через defer, а GSAP загружается только в момент когда прелоадер начинает скрываться. Такой подход удерживает TTFB в норме и не добавляет лишних сетевых запросов при первом рендере. В проектах с WebGL мы дополнительно отключаем three.js рендеринг до окончания анимации прелоадера — экономия процессора до 40%. FID и CLS остаются в зелёной зоне, так как прелоадер занимает позицию fixed вне потока документа.

Сроки и стоимость

Простой preloader с прогресс-баром и fade-out — 1–2 дня. Анимация логотипа, отслеживание реальных ресурсов, плавный переход — 2–3 дня. Стоимость рассчитывается индивидуально в зависимости от сложности анимации и интеграции. Мы — команда с 5-летним опытом в разработке сложных веб-интерфейсов. За нашими плечами более 50 реализованных проектов с анимацией.

Если вам нужен preloader под ваш проект — свяжитесь с нами. Мы оценим задачу и предложим оптимальное решение.