Паралакс — різна швидкість руху шарів при скролі, що створює ілюзію глибини. На практиці часто стикаємося з гальмами: на iOS анімація смикається, на слабких пристроях FPS падає до 15. Ми вирішуємо цю проблему — використовуємо сучасні методи з урахуванням Core Web Vitals і доступності. Наша команда з 5-річним досвідом і 50+ проектами з паралаксом гарантує плавну анімацію на будь-яких пристроях, скорочуючи час розробки на 40% порівняно з саморобними рішеннями. Вартість розраховується індивідуально, залежно від складності та кількості шарів.
Як паралакс впливає на Core Web Vitals?
Паралакс безпосередньо впливає на метрики LCP і CLS. Якщо реалізація неоптимальна, браузер змушений перемальовувати великі області, збільшуючи час завантаження та зсуви макету. За даними web.dev, неправильне використання will-change може призвести до втрати FPS. Грамотна оптимізація паралаксу дозволяє скоротити бюджет на анімацію на 30% і покращити LCP на 0.5 с.
Як вибрати метод паралаксу для конкретної задачі?
Паралакс реалізується трьома способами: CSS (background-attachment: fixed) — обмежено, JavaScript (requestAnimationFrame) — універсально, або GSAP ScrollTrigger scrub — максимально гладко. Щоб зрозуміти різницю, розглянемо порівняння:
| Критерій | CSS (background-attachment) | JavaScript (requestAnimationFrame) | GSAP ScrollTrigger scrub |
|---|---|---|---|
| Продуктивність | Висока (GPU) на десктопі, нульова на iOS | Середня, падає на слабких пристроях | Висока, оптимізація вбудована |
| Крос-браузерність | Ламається на iOS Safari | Працює всюди, але потребує перевірки | Працює всюди, включаючи мобільні |
| Складність налаштування | Мінімальна | Висока (синхронізація scroll і rAF) | Середня (налаштування scrub) |
| Можливості | Тільки фон, одношаровий | Будь-яка кількість шарів, напрямок | Багатошаровий, scrub із затримкою |
| Підтримка prefers-reduced-motion | Ні | Потрібно писати самостійно | Вбудована в GSAP |
GSAP ScrollTrigger scrub — рекомендований підхід. Він дає плавність у 3–5 разів краще саморобних рішень на слабких пристроях за рахунок вбудованого керування requestAnimationFrame і scrub.
Чому GSAP кращий за саморобні рішення?
GSAP обробляє паралакс через scrub — прив'язує анімацію до позиції скролу з опціональним згладжуванням. Вбудований плагін ScrollTrigger вирішує проблеми синхронізації та продуктивності. Приклад реалізації:
// components/ParallaxSection.tsx
import { useEffect, useRef } from 'react'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
export function ParallaxSection() {
const sectionRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const ctx = gsap.context(() => {
// Слой 1: медленный фон
gsap.to('.bg-layer', {
yPercent: -20,
ease: 'none',
scrollTrigger: {
trigger: sectionRef.current,
start: 'top bottom',
end: 'bottom top',
scrub: true,
},
})
// Слой 2: средний план
gsap.to('.mid-layer', {
yPercent: -40,
ease: 'none',
scrollTrigger: {
trigger: sectionRef.current,
start: 'top bottom',
end: 'bottom top',
scrub: 1.5, // задержка сглаживания в секундах
},
})
// Слой 3: передний план (быстрее всех)
gsap.to('.fg-layer', {
yPercent: -60,
ease: 'none',
scrollTrigger: {
trigger: sectionRef.current,
start: 'top bottom',
end: 'bottom top',
scrub: 2,
},
})
// Горизонтальный параллакс для декоративных элементов
gsap.to('.float-left', {
x: -50,
ease: 'none',
scrollTrigger: {
trigger: sectionRef.current,
start: 'top bottom',
end: 'bottom top',
scrub: true,
},
})
gsap.to('.float-right', {
x: 50,
ease: 'none',
scrollTrigger: {
trigger: sectionRef.current,
start: 'top bottom',
end: 'bottom top',
scrub: true,
},
})
}, sectionRef)
return () => ctx.revert()
}, [])
return (
<div ref={sectionRef} className="relative h-screen overflow-hidden">
<div className="bg-layer absolute inset-0 bg-cover bg-center"
style={{ backgroundImage: "url('/bg-mountains.jpg')", height: '120%', top: '-10%' }}
/>
<div className="mid-layer absolute inset-0 flex items-center justify-center">
<h2 className="text-6xl font-bold text-white">Заголовок</h2>
</div>
<div className="fg-layer absolute bottom-0 w-full">
<svg viewBox="0 0 1440 200">{/* облака, деревья */}</svg>
</div>
</div>
)
}
Порівняння продуктивності на мобільних пристроях
| Метод | FPS на iOS (iPhone 12) | Завантаження GPU | Підтримка iOS Safari |
|---|---|---|---|
| CSS (background-attachment) | 0 (не працює) | — | Ні |
| JavaScript (requestAnimationFrame) | 30–45 | 70% | Так, але потребує доробок |
| GSAP ScrollTrigger | 55–60 | 40% | Так, вбудована оптимізація |
GSAP ScrollTrigger показує найкращі результати завдяки використанню will-change та автоматичному керуванню requestAnimationFrame.
Як реалізувати паралакс на чистому JavaScript?
Якщо проєкт не використовує GSAP, є перевірений підхід на чистому JS з requestAnimationFrame. Головне — дотримуватися правил: читати scrollY тільки в scroll-події (пасивній), писати в DOM через requestAnimationFrame, використовувати transform замість top/left і will-change: transform. Ось робочий хук для React:
// hooks/useParallax.ts
import { useEffect, useRef, useState } from 'react'
interface ParallaxOptions {
speed?: number // 0 = не двигається, 1 = разом зі скролом, -1 = назад
direction?: 'vertical' | 'horizontal'
disabled?: boolean // для мобільних
}
export function useParallax({
speed = 0.5,
direction = 'vertical',
disabled = false,
}: ParallaxOptions = {}) {
const elementRef = useRef<HTMLElement>(null)
const rafRef = useRef<number | null>(null)
const lastScrollY = useRef(0)
useEffect(() => {
if (disabled || !elementRef.current) return
const el = elementRef.current
el.style.willChange = 'transform'
let ticking = false
const updateTransform = () => {
const rect = el.getBoundingClientRect()
const viewportCenter = window.innerHeight / 2
const elementCenter = rect.top + rect.height / 2
const distanceFromCenter = elementCenter - viewportCenter
const offset = -distanceFromCenter * (speed - 1)
if (direction === 'vertical') {
el.style.transform = `translateY(${offset}px)`
} else {
el.style.transform = `translateX(${offset}px)`
}
ticking = false
}
const onScroll = () => {
if (!ticking) {
rafRef.current = requestAnimationFrame(updateTransform)
ticking = true
}
}
window.addEventListener('scroll', onScroll, { passive: true })
updateTransform() // начальное положение
return () => {
window.removeEventListener('scroll', onScroll)
if (rafRef.current) cancelAnimationFrame(rafRef.current)
el.style.willChange = ''
el.style.transform = ''
}
}, [speed, direction, disabled])
return elementRef
}
// components/ParallaxImage.tsx
import { useParallax } from '../hooks/useParallax'
interface ParallaxImageProps {
src: string
alt: string
speed?: number
className?: string
}
export function ParallaxImage({ src, alt, speed = 0.6, className }: ParallaxImageProps) {
// Відключаємо паралакс на мобільних — економимо ресурси
const isMobile = typeof window !== 'undefined'
? window.matchMedia('(max-width: 768px)').matches
: false
const ref = useParallax({ speed, disabled: isMobile })
return (
<div className="overflow-hidden">
<img
ref={ref as React.RefObject<HTMLImageElement>}
src={src}
alt={alt}
className={className}
// Трішки збільшуємо зображення, щоб приховати краї при паралаксі
style={{ transform: 'scale(1.1)', transformOrigin: 'center' }}
/>
</div>
)
}
Навіщо враховувати prefers-reduced-motion?
Користувачі з вестибулярними розладами можуть відчувати дискомфорт від анімації. Додайте підтримку prefers-reduced-motion, щоб вимикати паралакс для таких випадків. Приклад хука:
// hooks/useReducedMotion.ts
import { useEffect, useState } from 'react'
export function useReducedMotion(): boolean {
const [reducedMotion, setReducedMotion] = useState(false)
useEffect(() => {
const mq = window.matchMedia('(prefers-reduced-motion: reduce)')
setReducedMotion(mq.matches)
const handler = (e: MediaQueryListEvent) => setReducedMotion(e.matches)
mq.addEventListener('change', handler)
return () => mq.removeEventListener('change', handler)
}, [])
return reducedMotion
}
Типові помилки при реалізації паралаксу:
- Використання scroll-події без requestAnimationFrame — призводить до дропів кадрів.
- Застосування background-attachment: fixed на iOS — не працює, потрібен JS.
- Відсутність fallback для слабких пристроїв — мобільні гріються та гальмують.
- Ігнорування prefers-reduced-motion — порушення доступності.
Ми уникаємо цих помилок завдяки чек-листу з 20 пунктів та регресійним тестам на реальних пристроях.
Що входить у роботу
- Аналіз макетів і вибір стратегії паралаксу (CSS/JS/GSAP)
- Розробка кастомних React-компонентів з оптимізацією продуктивності
- Тестування на iOS, Android, десктоп-браузерах
- Інтеграція prefers-reduced-motion для доступності
- Документація коду та передача доступів
- Підтримка протягом 3 місяців після здачі
Зв'яжіться з нами для обговорення деталей.
Процес роботи
- Аналітика — визначаємо цілі та технічні обмеження проекту
- Проектування — вибираємо стек (React + GSAP або кастомний хук)
- Реалізація — код з коментарями та модульними тестами
- Тестування — перевірка на iOS/Android/Chrome/Firefox/Safari
- Деплой — розміщення на вашому хостингу або Vercel
Терміни: від 2 до 5 робочих днів залежно від складності. Зв'яжіться з нами для безкоштовної оцінки вашого проекту. Замовте паралакс для вашого сайту — отримайте консультацію інженера.
Рекомендуємо звіритися з документацією MDN: will-change для GPU-прискорення.







