Анимация контейнера с изображением при наведении мыши с использованием перспективы и преобразования

Анимация контейнера с изображением при наведении мыши с использованием перспективы и преобразования

От автора: я работаю над сайтом, на котором пользователю отображаются большие изображения. Вместо того, чтобы создавать для этих больших изображений стандартный эффект лайт-бокса (анимация увеличения на темном фоне), я решил попробовать сделать что-то более интерактивное и веселое. Я закончил делать изображение в контейнере, который наклоняется, когда пользователь наводит на него курсор мыши.

Вот окончательная версия:

Этот эффект достигается с помощью CSS и JavaScript. Я решил, что сделаю небольшое руководство, объясняющее, как работает каждая часть, чтобы вы могли легко воспроизвести это или расширить. Давайте приступим.

Настройка

Во-первых, нам нужен контейнер с еще одним внутренним элементом. Контейнер поможет нам с перспективой.

<div id="container"> <div id="inner"></div>
</div>

Для демонстрационных целей давайте поместим карточку точно по середине экрана:

body { /* Полная ширина и высота экрана */ width: 100%; min-height: 100vh; /* Устанавливаем контейнер по центру экрана */ display: flex; justify-content: center; align-items: center; margin: 0; background-color: rgb(220, 220, 220);
} #container { /* Это вступает в игру позже */ perspective: 40px;
} #inner { width: 20em; height: 18em; background-color: white
}

Это дает нам белую карточку, расположенную прямо в центре светло-серого фона. Обратите внимание, что мы установили перспективу для #container на 40px, это пока ничего не делает, потому что мы не создали никаких преобразований. Это будет рассмотрено позже в разделе JavaScript.

Давайте начнем писать скрипты

Вот, что мы делаем:

var container = document.getElementById('container');
var inner = document.getElementById('inner'); var onMouseEnterHandler = function(event) { update(event);
};
var onMouseLeaveHandler = function() { inner.style = "";
};
var onMouseMoveHandler = function(event) { if (isTimeToUpdate()) { update(event); }
}; container.onmouseenter = onMouseEnterHandler;
container.onmouseleave = onMouseLeaveHandler;
container.onmousemove = onMouseMoveHandler;

И вот, что все эти вещи делают (или будут делать):

Функции обработчика: эти функции обрабатывают события по мере их возникновения. Мы хотим решить, что происходит, когда курсор входит в, перемещается по и покидает контейнер, поэтому каждое из этих событий имеет обработчик.

Функция обновления: мы еще не создали ее, но целью этой фуекции будет обновление 3D-вращения нашего #inner div.

Функция времени для обновления: это еще одна функция, которую мы еще не создали, но она возвращает значение true, когда потребуется обновление. Это способ уменьшить количество вызовов функции update() и повысить производительность скрипта.

Событие: это объект JavaScript, который описывает событие, которое произошло.

Вышеприведенный код:

Обновляет 3D-поворот #inner div, как только мышь входит в контейнер.

Обновляет 3D-поворот #inner div, когда наступит соответствующее время — когда мышь перемещается по контейнеру.

Сбрасывает стиль #inner div, когда мышь покидает контейнер.

Пришло время для обновления?

Давайте добавим функцию, которая решает, когда нужно обновить 3D-поворот div #inner.

var counter = 0;
var updateRate = 10;
var isTimeToUpdate = function() { return counter++ % updateRate === 0;
};

Когда counter достигнет updateRate, будет выполнено обновление. На этом этапе вы можете попробовать заменить функцию обновления на console.log() и поэкспериментировать с updateRate, чтобы увидеть, как все это работает.

Мышь

Далее нам нужен объект мыши. Это немного сложнее. Тем не менее, это не так сложно понять, но код может показаться пугающим, особенно если вы новичок в JavaScript.

// Init
var container = document.getElementById('container');
var inner = document.getElementById('inner');
// Mouse var mouse = { _x: 0, _y: 0, x: 0, y: 0, updatePosition: function(event) { var e = event || window.event; this.x = e.clientX - this._x; this.y = (e.clientY - this._y) * -1; }, setOrigin: function(e) { this._x = e.offsetLeft + Math.floor(e.offsetWidth/2); this._y = e.offsetTop + Math.floor(e.offsetHeight/2); }, show: function() { return '(' + this.x + ', ' + this.y + ')'; }
}
// Track the mouse position relative to the center of the container.
mouse.setOrigin(container);

Опять же, давайте разберем этот код:

show(): отображает текущую позицию мыши (если вы хотите выполнить некоторую отладку в консоли браузера).

setOrigin(e): Устанавливает координаты (0,0) для нашего объекта мыши в центре элемента (e).

updatePosition(): обновляет текущую позицию нашего объекта мыши относительно (0,0).

Последняя строка кода ouse.setOrigin(container) привязывает координаты (0,0) нашего объекта мыши к центру контейнера. Вот пример, который иллюстрирует это.

Идея состоит в том, чтобы добавить дополнительное вращения для #inner div, когда вы перемещаете мышь дальше от центра контейнера.

Обновляем стили для позиции мыши

Вот функция обновления:

var update = function(event) { mouse.updatePosition(event); updateTransformStyle( (mouse.y / inner.offsetHeight/2).toFixed(2), (mouse.x / inner.offsetWidth/2).toFixed(2) );
}; var updateTransformStyle = function(x, y) { var style = "rotateX(" + x + "deg) rotateY(" + y + "deg)"; inner.style.transform = style; inner.style.webkitTransform = style; inner.style.mozTransform = style; inner.style.msTransform = style; inner.style.oTransform = style;
};

update(): обновляет положение мыши и стиль #inner div.

updateTransformStyle(): обновляет стиль для каждого вендорного префикса.

Мы закончили?

Давайте попробуем! Похоже, мы получаем изменения перспективы, когда курсор мыши входит и выходит в область карточки, но это происходит не так плавно, как могло бы быть:

А, верно! Мы указали ему обновлять вращение #inner div каждый раз, когда счетчик достигает updateRate. Это создает неуклюжий переход между обновлениями. Как мы это решим? Переходы CSS.

Добавление переходов

#inner { transition: transform 0.5s;
}

Это произвольные числа. Вы можете поэкспериментировать со значениями перспективы и преобразований, чтобы сделать эффект более или менее драматичным — как сочтете нужным.

Обратите внимание, что изменение размера страницы приведет к некоторым проблемам, так как изменяется позиция контейнера на странице. Решение состоит в том, чтобы повторно отцентрировать объект мыши в контейнере после изменения размера страницы.

Заключение

Мы закончили! Теперь у нас есть контейнер, который делает элемент немного более интерактивным. Демо-версия в начале этой статьи использует изображение внутри контейнера, но это может использоваться для других вещей, не только изображений: включая формы, модальные окна или любой другой контент, который вы поместите в контейнер. Попробуйте поэкспериментировать!

Автор: Mihai Ionescu

Источник: https://css-tricks.com/

Редакция: Команда webformyself.