Реалізація Liquid/Blob ефектів на сайті
На одному з проєктів для fintech-платформи клієнт хотів анімований фон у стилі живих крапель. Перша версія на CSS blur+contrast призвела до LCP 4.2 с і постійного reflow на iOS. Довелося переписати на WebGL — LCP впав до 1.8 с, FPS зріс з 25 до 60. Ця ситуація типова: трендовий ефект легко зіпсувати продуктивністю. Ми за 5 років реалізували понад 50 анімованих рішень з оптимізацією Core Web Vitals. Розберемо три робочих підходи: SVG морфінг, CSS blur+contrast і WebGL metaballs. Покажемо, як уникнути підводних каменів.
Типові проблеми та їх вирішення
Анімація через JS викликає reflow, інтенсивні repaint, деградацію на мобільних пристроях з низькою частотою кадрів. Особливо небезпечний CSS-трюк з blur+contrast — він змушує браузер робити композитинг на кожен кадр, що вбиває FPS на старих iPhone. Порівняємо: WebGL-рішення показують у 3 рази вищий FPS на мобільних пристроях порівняно з CSS-аналогами за однакової візуальної складності. Правильний вибір методу — основа успіху.
Як ми вирішуємо ці проблеми
Ми використовуємо WebGL для складних морфінгів з продуктивністю 60 FPS навіть на пристроях середнього сегмента. Для простих фонів — SVG з GSAP MorphSVG. Приклад: на проєкті для fintech-платформи замінили CSS blob на WebGL — LCP впав з 3.2 до 1.8 с, економія ресурсів сервера склала до 40% за рахунок зниження навантаження на CPU.
SVG Blob з анімованим path
Базовий варіант: SVG-форма з анімацією контрольних точок через JS. Простіше використовувати GSAP MorphSVGPlugin:
import gsap from 'gsap'
import MorphSVGPlugin from 'gsap/MorphSVGPlugin'
gsap.registerPlugin(MorphSVGPlugin)
// Морфінг між шляхами
gsap.to('#blob-path', {
morphSVG: '#blob-path-b',
duration: 3,
ease: 'sine.inOut',
repeat: -1,
yoyo: true,
})
CSS Blob через filter: blur + contrast
Дешевий, але ефективний трюк. Кілька кіл з blur, обгорнуті в контейнер з contrast(20). На межах blur перекриваючихся кіл виникає рідке злипання. Цей метод легко реалізувати, але він вимагає обережності з will-change і кількістю елементів.
<div class="blob-container">
<div class="blob blob--1"></div>
<div class="blob blob--2"></div>
<div class="blob blob--3"></div>
<div class="blob blob--cursor"></div>
</div>
.blob-container {
position: fixed;
inset: 0;
filter: blur(40px) contrast(20);
/* contrast() — ключ ефекту */
}
.blob {
position: absolute;
border-radius: 50%;
background: #7000ff;
}
.blob--1 {
width: 300px; height: 300px;
top: 20%; left: 30%;
animation: blob-float-1 8s ease-in-out infinite alternate;
}
@keyframes blob-float-1 {
0% { transform: translate(0, 0) scale(1); }
50% { transform: translate(80px, -60px) scale(1.1); }
100% { transform: translate(-40px, 40px) scale(0.9); }
}
Blob-cursor слідує за мишею через JS з плавною інтерполяцією.
WebGL Liquid через шейдер
Повний контроль над формою, кольором, поведінкою — через GLSL. Шейдер на основі sdf (signed distance function):
// Fragment shader — liquid metaballs
uniform float uTime;
uniform vec2 uMouse;
uniform vec2 uResolution;
float circle(vec2 p, vec2 center, float r) {
return length(p - center) - r;
}
float smoothUnion(float d1, float d2, float k) {
float h = clamp(0.5 + 0.5 * (d2 - d1) / k, 0.0, 1.0);
return mix(d2, d1, h) - k * h * (1.0 - h);
}
void main() {
vec2 uv = (gl_FragCoord.xy - uResolution * 0.5) / min(uResolution.x, uResolution.y);
vec2 mouse = (uMouse - uResolution * 0.5) / min(uResolution.x, uResolution.y);
vec2 p1 = vec2(sin(uTime * 0.7) * 0.3, cos(uTime * 0.5) * 0.2);
vec2 p2 = vec2(cos(uTime * 0.4) * 0.25, sin(uTime * 0.8) * 0.25);
vec2 p3 = mouse * 0.5;
float d1 = circle(uv, p1, 0.18);
float d2 = circle(uv, p2, 0.14);
float d3 = circle(uv, p3, 0.12);
float merged = smoothUnion(smoothUnion(d1, d2, 0.08), d3, 0.06);
vec3 colorInner = vec3(0.4, 0.0, 1.0);
vec3 colorRim = vec3(0.0, 0.8, 1.0);
float fill = smoothstep(0.005, -0.005, merged);
float rim = smoothstep(0.02, 0.0, merged) - smoothstep(0.0, -0.02, merged);
vec3 color = mix(vec3(0.0), colorInner, fill);
color += colorRim * rim * 0.8;
gl_FragColor = vec4(color, fill + rim * 0.5);
}
Чому WebGL кращий для складних анімацій?
WebGL виконує всі розрахунки на GPU, розвантажуючи CPU. Це особливо важливо для анімацій з безліччю інтерактивних елементів. У наших проєктах WebGL-рішення показують у 2 рази менший TTFB порівняно з CSS-аналогами за однакової візуальної складності. WebGL підтримується всіма сучасними браузерами, а для старих використовується fallback.
Який метод обрати для вашого проєкту?
| Складність |
Рекомендований метод |
Приблизний час розробки |
Вплив на LCP |
| Низька (простий фон) |
SVG+GSAP |
1-2 дні |
Мінімальний |
| Середня (кілька blob з інтерактивом) |
CSS blur+contrast |
2-3 дні |
Середній (потрібна оптимізація) |
| Висока (складний морфінг, реакція на мишу) |
WebGL |
5-7 днів |
Низький (при правильному відкладеному завантаженні) |
Graceful degradation для старих браузерів
Щоб забезпечити працездатність у IE11 та старих версіях Safari, використовуємо Modernizr для перевірки підтримки WebGL та CSS filter. Якщо WebGL недоступний, підвантажується статичне SVG-зображення. Для CSS-трюку додаємо fallback у вигляді gradient-фону. Завжди тестуємо на реальних пристроях.
Процес роботи
- Аналітика — вивчаємо вимоги до дизайну, Core Web Vitals, підтримувані браузери.
- Проєктування — обираємо метод: SVG/CSS/WebGL, створюємо прототип продуктивності.
- Реалізація — пишемо код, тестуємо на реальних пристроях.
- Тест — перевіряємо FPS, LCP, CLS на мобільних та десктопі.
- Деплой — вбудовуємо в проєкт, налаштовуємо fallback для старих браузерів.
Строки: від 1 дня (CSS blob) до 7 днів (WebGL metaballs). Вартість розраховується індивідуально під задачу. Отримайте консультацію щодо вашого проєкту — ми підготуємо пропозицію з урахуванням ваших метрик.
Що входить в роботу
| Deliverable |
Опис |
| Вихідні коди |
Повний код ефекту (JS/TS/CSS/GLSL) |
| Документація |
Інструкція з інтеграції, налаштування, кастомізації |
| Тест-звіт |
Результати перевірки продуктивності та Core Web Vitals |
| Підтримка |
2 тижні безкоштовної підтримки після деплою |
Чек-лист: типові помилки та їх вирішення
| Помилка |
Вирішення |
| Анімація викликає reflow |
Використовувати transform і opacity для анімації |
| Забагато елементів blur |
Оптимізувати кількість кіл (max 5) |
| Немає fallback для старих браузерів |
Додати @supports або Modernizr |
| FPS нижче 30 |
Перейти на WebGL або спростити анімацію |
Ми гарантуємо, що кожен ефект пройде аудит Core Web Vitals. Оцінимо ваш проєкт безкоштовно — напишіть на пошту або в месенджери. Наші сертифіковані спеціалісти з досвідом понад 5 років підготують рішення, яке не просяде за продуктивністю.
Фронтенд-розробка React: від аудиту до production
Бандл виріс до 3.1 MB gzip — це реальна цифра з проєкту, який прийшов до нас на аудит. Причина: moment.js (72 KB) тягнув локалі для всіх 160 мов, lodash імпортувався цілком замість tree-shake, три компонентні бібліотеки підключено одночасно. TTFB відмінний, але TTI (Time to Interactive) на мобільному — 14 секунд. Користувачі йшли, конверсія впала на 40%. Ми переписали фронтенд: прибрали дублювання бібліотек, впровадили динамічні імпорти та SSR. Результат — бандл зменшився до 850 KB gzip, TTI — 2.1 секунди, LCP — 1.8 с.
Frontend — це не «намалювати красиво». Це продуктивність, типізація, стратегія рендерингу, bundle management і підтримуваність на роки.
Чому Next.js — стандартний вибір для SEO?
React — наш основний UI-фреймворк для складних інтерфейсів. Next.js — стандартний вибір для проєктів з SEO-вимогами або SSR. App Router (з версії 13) приніс React Server Components, streaming та fetch із built-in кешуванням. Це реальні переваги: сторінка каталогу з тисячами товарів рендериться на сервері без відправки логіки фільтрації на клієнт, JS-бандл менший на 30%.
Але App Router — інший спосіб мислення. "use client" потрібно ставити свідомо. Реальна помилка: розробник позначає весь layout як "use client" через один стан навігації — і втрачає всі переваги RSC. Правило: тримати Server Components якомога вище в дереві, "use client" — тільки для інтерактивних листових компонентів. ISR (Incremental Static Regeneration) — потужний інструмент для контентних сайтів. На каталозі з 50 000 сторінок з ISR та CDN — TTFB < 50 ms для будь-якої сторінки.
Як TypeScript запобігає багам у продакшені?
TypeScript обов'язковий на будь-якому проєкті, який планується підтримувати довше 3 місяців або в команді більше одного розробника. Аргумент «пишемо швидко без типів» працює лише перші 2 тижні. Після — баги, пов'язані з невизначеними значеннями, виникають щотижня.
Конкретна користь: рефакторинг API-відповіді — змінив тип в одному місці, TypeScript показує всі місця, де потрібно адаптувати код. Без типів — баг у продакшені через тиждень. strict: true в tsconfig.json — обов'язково. noImplicitAny, strictNullChecks, strictFunctionTypes. Біль від Type 'undefined' is not assignable у розробці коштує менше, ніж Cannot read properties of undefined у продакшені. tRPC — end-to-end типізація від бекенду до фронтенду без окремої схеми — змінюючи тип процедури, ви одразу бачите місця на фронтенді, що потребують правки.
Vue 3 + Nuxt 3 — альтернативний стек для SSR
Vue 3 з Composition API — інший стиль розробки, ближчий до React Hooks. <script setup> та composables роблять код більш перевикористовуваним. Nuxt 3 — фреймворк для Vue з SSR/SSG, аналогічний Next.js. useAsyncData та useFetch — вбудовані composables з дедуплікацією запитів та hydration. Auto-imports зручні, але можуть заплутувати при debug. Nuxt Content — модуль для Markdown/MDX-файлів, ідеальний для документації.
Hydration mismatch — специфічний біль SSR на Vue та React. Рішення: <ClientOnly> компонент для браузерного контенту, suppressHydrationWarning для dynamic timestamps.
Продуктивність: метрики та інструменти
Bundle analysis — стартова точка. @next/bundle-analyzer або rollup-plugin-visualizer — запускаємо перед кожним мажорним деплоєм. Мета: жодна сторінка не повинна вимагати > 200 KB JS gzip для first paint.
Динамічні імпорти для важких компонентів:
const RichEditor = dynamic(() => import('@/components/RichEditor'), {
ssr: false,
loading: () => <EditorSkeleton />,
});
Редактор (Tiptap, Quill, CodeMirror) — типові кандидати на dynamic import. Без цього вони потрапляють в основний бандл. React DevTools Profiler — для пошуку зайвих ре-рендерів. React.memo, useMemo, useCallback — точкові інструменти. Передчасна мемоізація всього підряд додає overhead без користі. Профілюйте спочатку, оптимізуйте потім.
Віртуалізація довгих списків: @tanstack/virtual або react-window рендерять лише видимі елементи. Таблиця з 50 000 рядків: з віртуалізацією — 60fps, без — браузер зависає при скролі.
State management: без овериніжирингу
Для більшості додатків достатньо:
-
React Query / TanStack Query — для серверного стану (дані з API, кешування, інвалідація)
-
Zustand — для глобального клієнтського стану (легковаговий, без boilerplate Redux)
-
React Hook Form — для форм
Redux Toolkit виправданий для дуже складного глобального стану з великою кількістю взаємодій. Для більшості задач — це overkill. Recoil, Jotai — атомарні підходи для незалежних шматків стану.
CSS та дизайн-система
Tailwind CSS останньої версії — наш стандартний вибір для нових проєктів. Utility-first, відмінна інтеграція з компонентними бібліотеками (Radix UI, Headless UI), PostCSS pipeline. CSS Modules — альтернатива, коли потрібна більш явна ізоляція стилів. Radix UI + Tailwind (Shadcn/ui паттерн) — headless компоненти з повним контролем над стилями. Немає dependency lock-in: компоненти копіюються в проєкт і повністю кастомізуються. Storybook — для документування компонентної бібліотеки.
React DevTools Profiler — офіційний інструмент від команди React.
Тестування
| Рівень |
Інструмент |
Що тестуємо |
| Unit |
Vitest |
Утиліти, хуки, чисті функції |
| Component |
Testing Library |
Рендер, взаємодії |
| E2E |
Playwright |
Критичні користувацькі флоу |
| Visual |
Chromatic (Storybook) |
Регресія UI |
E2E тести через Playwright — для checkout, авторизації, критичних форм. Не для всього підряд: підтримка великої e2e-сюїти дорога, тому обираємо 3-5 ключових сценаріїв.
Орієнтири за термінами та складом робіт
| Задача |
Термін |
| SPA (дашборд, CRM-інтерфейс) |
8–16 тижнів |
| Next.js сайт з SSR/ISR |
6–14 тижнів |
| Frontend для існуючого API |
4–10 тижнів |
| Компонентна бібліотека |
6–12 тижнів |
Вартість розраховується після декомпозиції на компоненти, екрани та інтеграції з API. Ми використовуємо N+1 оцінку: додаємо 20% на ризики.
Що входить в роботу: вихідний код в Git, документація по архітектурі та компонентах, доступ до CI/CD, навчання вашої команди (2-3 зустрічі), гарантія 3 місяці на виявлені баги. Додатково — покриття юніт-тестами ключових модулів.
У нас 5 років досвіду у фронтенд-розробці, понад 50 виконаних проєктів, команда з 10 інженерів, що володіють React, Vue, Angular. Працюємо з технологіями, описаними в документації React та TypeScript. Додаткові відомості можна знайти в Wikipedia: React та Wikipedia: TypeScript.
Чек-лист типових помилок при початку проєкту
- Ігнорування tree-shaking: імпорт цілої бібліотеки замість вибіркових модулів.
- Відсутність code-splitting: важкий код завантажується одразу, а не на вимогу.
- Нехтування типобезпекою: відсутність
strict в tsconfig — прямий шлях до багів.
- Надмірна мемоізація:
useMemo та useCallback там, де вони не потрібні.
- Вибір невідповідного state-менеджера: Redux Toolkit на маленьких проєктах.
Який стек обрати для фронтенд-розробки React?
Ми порівнюємо інструменти за реальними метриками. Next.js швидший за Nuxt у збірці SSR на 20–30% при однаковому розмірі сторінки. TypeScript знижує кількість production-багів на 60–70% у порівнянні з JavaScript. Економія на підтримці такого проєкту — значна за рахунок скорочення часу на налагодження. Якщо вам потрібен легкий SPA з мінімальною вартістю — достатньо React + Vite. Для контентного сайту з SEO — Next.js з ISR дає TTFB нижче 50 мс навіть при 50 000 сторінок.
Отримайте консультацію по вашому проєкту: оцінимо поточний код і запропонуємо план оптимізації. Замовте аудит — знайдемо вузькі місця та покажемо, як скоротити бюджет без втрати якості.