От автора: в последнее время ряд сайтов, в том числе рекламная страница Bioware игры Mass Effect Andromeda и работа агентства Active Theory Under Armour получили параллакс эффект, когда движение мыши связано с движением элемента на странице в противоположном направлении. Я захотел воссоздать этот эффект на чистом JavaScript и современных свойствах CSS. Пример моих стараний можно увидеть ниже.
Итоговое демо урока.
Выходим на ринг
Разметка состоит из контейнера div, изображения, заголовка и основного текста (взятого из произведения Джека Лондона Кусок мяса) внутри еще одного div:
1 2 3 4 5 6 7 |
<div id="boxercontainer"> <img src="boxer.png" alt> <div> <h1>The Boxer</h1> <p>Well, a man had only so many fights in him, to begin with… </div> </div> |
Боксера мы отделили от фона и сохранили в PNG с альфа маской. С помощью конвертации изображения в 16-bit мы уменьшили размер файла.
Стили внешнего элемента #boxercontainer: обратите внимание, что background-image сделан немного больше контейнера и расположен чисто по центру. Это очень важно, но об этом позже:
1 2 3 4 5 6 7 8 9 10 11 12 |
#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 задана прозрачность, светло-серый фон для увеличения контрастности текста:
1 2 3 4 5 6 7 8 9 |
#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. Как мы сейчас увидим, такая тень делает изображение более динамичным.
1 2 3 4 5 6 7 8 9 |
#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 с текстом, но из-за этого тень будет падать на слова. Чтобы поправить это, я изменил текст:
1 2 3 4 |
#boxercontainer p { position: relative; z-index: 3; } |
Значение z-index у текста теперь больше, и тень проваливается под текст, но расположена выше рамки блока div.
Центрируем
Основная идея параллакс эффекта заключается в том, чтобы найти центр затрагиваемого элемента. Расстояние от центра до курсора будет смещать элемент на такое же расстояние, но в противоположную сторону.
Скрипт начинает с идентификации элементов. Атрибут id в JS автоматически превращается в ссылку, а для получения боксера используется querySelector:
1 2 3 4 |
const boxer = boxercontainer.querySelector("img"), maxMove = boxercontainer.offsetWidth / 30, boxerCenterX = boxer.offsetLeft + (boxer.offsetWidth / 2), boxerCenterY = boxer.offsetTop + (boxer.offsetHeight / 2); |
Переменная maxMove – максимальное расстояние, на которое можно сдвинуть боксера. Обычно изображению не дают полной свободы на сдвиг относительно курсора, так как курсор может быть в любой части страницы.
Кроме того, нам нужно определить положение мыши внутри boxercontainer, для чего я использую функцию:
1 2 3 4 5 6 7 8 9 |
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 }; } |
Порхай как бабочка
Такие эффекты обычно реагируют на движение мыши на странице:
1 2 3 4 5 6 7 8 9 |
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 нужно добавить код ниже в конец скрипта:
1 2 |
boxercontainer.style.backgroundPosition = `calc(50% + ${distX / 50}px) calc(50% + ${distY / 50}px)`; |
Чтобы заставить фон двигаться, я использую функцию calc, с помощью которой вычисляю сдвиг относительно центра изображения по умолчанию. Для облегчения конкатенации я использую шаблонные строки.
Я двигаю изображение на основе положения мыши, а не касаний, так как взрослые, как правило, не лапают постоянно экран.
Исключение
Есть общее правило. Параллакс эффект хорошо работает на больших экранах и ноутбуках и совсем не работает на маленьких разрешениях. Эффект создан на JS, поэтому мы должны определять ширину экрана с помощью matchMedia:
1 |
let fluidboxer = window.matchMedia("(min-width: 726px)"); |
Условие if для движения изображения и фона меняется на:
1 |
if (Math.abs(distX) < 500 && distY < 200 && fluidboxer.matches) { … } |
В CSS @media запросах есть дополнительные изменения для смены дизайна на маленьких экранах. Более подробно в CodePen демо.
Для людей с различными расстройствами
Параллакс эффект может оказать негативное воздействие на людей с расстройствами вестибулярного аппарата (чувствительных к визуальному движению), вплоть до тошноты и припадков. Было бы хорошо, как минимум, показывать предупреждение или какой-то другой знак того, что на странице есть эффект движения. Разработчики браузеров сейчас работают над настройкой «уменьшить движение». В ближайшем будущем движение будет определяться с помощью @media запроса. Эффекты типа палаллакса будут автоматически выключаться на страницах для людей с такими расстройствами.
Заключение
CSS трансформации и JS события движения мыши – относительно простой и легкий способ создания параллакс эффекта с минимумом вычислений. В будущих статьях я более подробно раскрою эффекты такого типа.
Источник: //thenewcode.com/
Редакция: Команда webformyself.