Легкий паралакс ефект за допомогою JavaScript і CSS трансформацій

36

Від автора: останнім часом ряд сайтів, в тому числі рекламна сторінка Bioware гри Mass Effect Andromeda і робота агентства Active Theory Under Armour отримали ефект паралакса, коли рух миші пов’язано з рухом елемента на сторінці в протилежному напрямку. Я захотів відтворити цей ефект на чистому JavaScript і сучасних властивості CSS. Приклад моїх старань можна побачити нижче.

Підсумкове демо уроку.

Виходимо на ринг

Розмітка складається з контейнера div, зображення, заголовка та основного тексту, взятого з твору Джека Лондона Шматок м’яса) всередині ще одного div:

The Boxer

Well, a man had only so many fights him in, to begin with…

Боксера ми відокремили від фону і зберегли в PNG з альфа маскою. З допомогою конвертації зображення в 16-bit ми зменшили розмір файлу.

Стилі зовнішнього елемента #boxercontainer: зверніть увагу, що background-image зроблений трохи більше контейнера і розташований чисто по центру. Це дуже важливо, але про це пізніше:

#boxercontainer {
width: 80%;
max-width: 900px;
margin: 0 auto;
background-image: url(concrete-background.jpg);
position: relative;
padding-bottom: 45%;
background-size: 120% 120%;
background-position: 50% 50%;
overflow: hidden;
min-height: 650px;
}

У внутрішнього блоку div задана прозорість, світло-сірий фон для збільшення контрастності тексту:

#boxercontainer div {
position: absolute;
width: 60%;
left: 20px;
top: 20px;
border: 1px solid #fff;
padding: 2rem;
background: rgba(0,0,0,0.2);
}

Зображення позиціоновано абсолютно і опущене так, щоб нижня частина ніг боксера виходила за межі батьківського елемента і ховалася за допомогою overflow: hidden. Навколо боксера застосований фільтр drop-shadow, який дає набагато менший розмір файлу, ніж малювати тінь в PNG. Як ми зараз побачимо, чи така тінь робить зображення більш динамічним.

#boxercontainer img {
position: absolute;
bottom: -35px;
right: 50px;
width: 40%;
filter: drop-shadow(-200px 200px 50px #000);
padding: 1rem;
z-index: 2;
}

Боксеру присвоєно властивість z-index = 2, що поміщає його візуально вище блоку div з текстом, але з-за цього тінь буде падати на слова. Щоб виправити це, я змінив текст:

Легкий параллакс эффект с помощью JavaScript и CSS трансформаций

#boxercontainer p {
position: relative;
z-index: 3;
}

Значення z-index тексту тепер більше, і тінь провалюється під текст, але розташована вище рамки блоку div.

Центрируем

Основна ідея паралакс ефекту полягає в тому, щоб знайти центр затрагиваемого елемента. Відстань від центра до курсору буде зміщати елемент на таку ж відстань, але в протилежну сторону.

Легкий параллакс эффект с помощью JavaScript и CSS трансформаций

Скрипт починає з ідентифікації елементів. Атрибут id JS автоматично перетворюється на заслання, а для отримання боксера використовується querySelector:

const boxer = boxercontainer.querySelector(«img»),
maxMove = boxercontainer.offsetWidth / 30,
boxerCenterX = boxer.offsetLeft + (boxer.offsetWidth / 2),
boxerCenterY = boxer.offsetTop + (boxer.offsetHeight / 2);

Змінна maxMove – максимальна відстань, на яке можна зрушити боксера. Зазвичай зображення не дають повної свободи на зрушення щодо курсору, так як курсор може бути в будь-якій частині сторінки.

Крім того, нам потрібно визначити положення миші всередині boxercontainer, для чого я використовую функцію:

function getMousePos(xRef, yRef) {
let panelRect = boxercontainer.getBoundingClientRect();
return {
x: Math.floor(xRef — panelRect.left) /
(panelRect.right — panelRect.left)*boxercontainer.offsetWidth,
y: Math.floor(yRef — panelRect.top) /
(panelRect.bottom — panelRect.top) * boxercontainer.offsetHeight
};
}

Порхай як метелик

Такі ефекти зазвичай реагують на рух миші на сторінці:

document.body.addEventListener. («mousemove», function(e) {
let mousePos = getMousePos(e.clientX, e.clientY),
distX = mousePos.x — boxerCenterX,
distY = mousePos.y — boxerCenterY;
if (Math.abs(distX) < 500 && distY < 200) {
boxer.style.transform =
«translate(«+(-1 * distX) / 12 + «px,» + (-1 * distY) / 12 + «px)»;
}
})

Змінна distX – горизонтальна відстань від поточного положення миші до споконвічного центру зображення боксера. Змінна distY – вертикальна відстань. Якщо різниця по вертикалі (позитивна або негативна) менше 500px, а горизонтальне відстань менше 200px, ми рухаємо боксера з допомогою CSS трансформацій:

обидва відстані множаться на -1 (позитивне значення стає від’ємним і навпаки);

результат ділиться на 12, щоб зменшити відношення руху між мишею і зображенням (співвідношення 1:1 між положенням миші і зображенням призведе до дуже різких рухів).

Зараз варто сказати, що я ніколи б не запропонував використовувати цю техніку для UI елементів: потрапити по рухомій цілі дуже складно для користувачів з вадами зору й руху. Можливе рішення описано нижче.

Коли ми рухаємо голову, позицію змінюють не тільки елементи переднього плану. Об’єкти на задньому фоні також зміщуються в протилежну сторону. Уявіть об’єкт, розташований в декількох сантиметрах від стіни: перемістивши об’єкт вправо, для вас перспектива зміниться для стіни і об’єкта.

Щоб відтворити такий ефект, я зрушую background-image бетонної стіни. У колбек функцію addEventListener. потрібно додати код нижче в кінець скрипта:

boxercontainer.style.backgroundPosition =
`calc(50% + ${distX / 50}px) calc(50% + ${distY / 50}px)`;

Щоб змусити фон рухатися, я використовую функцію calc, з допомогою якої вираховую зсув відносно центру зображення за замовчуванням. Для полегшення конкатенації я використовую шаблонні рядка.

Я рухаю зображення на основі положення миші, а не торкань, так як дорослі, як правило, не лапають постійно екран.

Виняток

Є загальне правило. Паралакс ефект добре працює на великих екранах і ноутбуках і зовсім не працює на маленьких дозволах. Ефект створений на JS, тому ми повинні визначати ширину екрану з допомогою matchMedia:

let fluidboxer = window.matchMedia(«(min-width: 726px)»);

Умова для руху зображення і фону змінюється на:

if (Math.abs(distX) < 500 && distY < 200 && fluidboxer.matches) { … }

В CSS @media запитах є додаткові зміни для зміни дизайну на маленьких екранах. Більш докладно в CodePen демо.

Для людей з різними розладами

Паралакс ефект може чинити негативний вплив на людей з розладами вестибулярного апарату (чутливих до візуального руху), аж до нудоти і припадків. Було б добре, як мінімум, показувати попередження або якийсь інший знак того, що на сторінці є ефект руху. Розробники браузерів зараз працюють над налаштуванням «зменшити рух». У найближчому майбутньому рух буде визначатися за допомогою @media запиту. Ефекти типу палаллакса будуть автоматично вимикатися на сторінках для людей з такими розладами.

Висновок

CSS трансформації і JS події руху миші – відносно простий і легкий спосіб створення паралакс ефекту з мінімумом обчислень. В майбутніх статтях я більш детально розкрию ефекти такого типу.