Статьи

Создание ощущения 3D с помощью свойства Perspective CSS3

Конечный продукт
Что вы будете создавать

В этом уроке мы создадим интерактивный элемент со свойством перспективы CSS3, чтобы дать представление о трех измерениях. Этот урок также научит вас, как использовать jQuery с событиями мыши для получения позиций элементов в JavaScript и как манипулировать свойствами CSS.

Нам нужно, чтобы родительские и дочерние отношения работали правильно. Давайте сначала создадим структуру HTML, а затем продолжим стилизацию CSS.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div id=»mainWrapper»>
  <div id=»cardsWrapper»>
     
    <div class=»card»>
      <div class=»image first»>
        <div class=»screen»></div>
        <div class=»text»>
            <p>Mountain</p>
            <p>5 Days</p>
        </div>
      </div>
    </div>
     
    <div class=»card»>
      <div class=»image second»>
        <div class=»screen»></div>
        <div class=»text»>
            <p>Island</p>
            <p>2 Days</p>
        </div>
      </div>
    </div>
     
  </div>
</div>

Здесь мы заключаем два элемента card в div с идентификатором cardsWrapper . Кроме того, этот cardsWrapper обернут в другой div, чтобы иметь возможность легко манипулировать его положением в области просмотра.

Каждый элемент с классом card имеет элемент image , который охватывает screen и text элементы. Структура пока немного расплывчата, но я объясню использование каждого элемента в соответствующих следующих разделах.

Давайте использовать следующий стиль CSS для позиционирования наших элементов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#mainWrapper{
  display: flex;
  justify-content: center;
  align-items: center;
  height: 350px;
}
 
#cardsWrapper{
  display: flex;
  justify-content: space-between;
  width: 700px;
}
 
.card{
  width: 300px;
  height: 175px;
  background-color: red;
}
 
.image{
  width: 100%;
  height: 100%;
  background-color: red;
}

Свойство perspective — это то, что вы должны установить в родительском элементе div, который содержит элементы div, которые вы хотите преобразовать с учетом перспективы. Представьте, что родительский div — это ваш мир, и он имеет определенную перспективную ценность, которую вы испытываете.

Давайте добавим perspective свойство к нашему родительскому div, который является card . Мы cardsWrapper элемент card как родительский, а не cardsWrapper , поскольку мы хотим, чтобы каждый элемент карты имел индивидуальный эффект перспективы.

Настройте CSS card следующим образом.

1
2
3
4
5
6
.card{
  width: 300px;
  height: 175px;
  background-color: red;
  perspective: 500px;
}

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

1
2
3
4
5
6
.image{
  width: 100%;
  height: 100%;
  background-color: red;
  transform: rotateX(30deg);
}

Поскольку image является прямым потомком card , оно зависит от перспективы. Однако, если вы попытаетесь добавить свойство transform к любому дочернему элементу image , это не будет работать.

Чтобы эти дочерние элементы были преобразованы относительно их родителя, который является элементом image в этом примере, вы должны использовать transform-style: preserve-3d на родительском элементе.

1
2
3
4
5
6
.image{
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform: rotateX(30deg);
}

Теперь у нас есть достаточный фон в свойстве перспективы и мы готовы продолжить стилизацию других элементов.

Не забудьте удалить transform: rotateX(30deg) из вашего элемента изображения.

У нас есть элемент image , и поверх него есть элемент с именем screen , а затем text . Поскольку мы используем здесь перспективу, вы можете думать о каждом из этих элементов как об отдельных слоях.

Теперь мы добавим фоновое изображение к нашему image div и затем screen элементы screen и text .

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

1
2
3
4
5
6
7
.image.first{
  background-image: url(«https://c1.staticflickr.com/1/343/31652757460_b2b5794a51_n.jpg»);
}
 
.image.second{
  background-image: url(«https://c2.staticflickr.com/2/1506/25121644830_2d768ef51a_n.jpg»);
}

Теперь мы будем screen элемент screen .

Поскольку мы хотим, чтобы элемент screen был точно такого же размера, как и его родительский элемент image , мы используем 100% значения ширины и высоты и серый черный цвет фона с альфа-каналом.

Импортируемая часть — это transform: translateZ(30px) scale(0.940) .

Здесь мы просто переводим элемент screen на оси Z на 20 пикселей. Это заставляет его парить над элементом image . Так как это к нам, это будет больше по размеру, из-за правил перспективы. Поэтому мы уменьшаем его до размера, соответствующего родительскому элементу. Если вы используете разные значения перевода, значение масштаба будет отличаться. Аналогично, определение различных размеров высоты и ширины для родительского элемента приведет к требованию другого значения масштабирования.

1
2
3
4
5
6
.screen{
  background-color: rgba(0, 0, 0, 0.22);
  width: 100%;
  height: 100%;
  transform: translateZ(30px) scale(0.940);
}

Чтобы понять, что здесь происходит, просто поверните элемент image вокруг осей X и Y, добавив следующую строку в правило CSS:

transform: rotateX(30deg) rotateY(30deg)

Теперь последняя часть этого раздела — стилизация text элемента, что довольно тривиально.

Мы в основном используем те же настройки преобразования для text элемента, чтобы он находился на том же уровне, что и элемент screen . Остальная часть CSS — это просто стилизация. Вы можете настроить его так, как вам нравится.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
.text{
  position: absolute;
  bottom: 25px;
  left: 30px;
  color: white;
  transform: translateZ(30px) scale(0.940);
}
 
.text p{
    cursor: default;
    padding: 0;
    margin: 0;
}
 
.text p:first-of-type{
    font-size: 2em;
    margin-bottom: 5px;
}
.text p:last-of-type{
    font-size: 1em;
}

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

Прежде чем продолжить, удалите правила вращения из вашего CSS, так как мы будем автоматически контролировать поворот в соответствии с положением курсора.

Теперь мы напишем некоторый код jQuery , чтобы сделать эти карты интерактивными.

Давайте доберемся до этого!

Давайте начнем с базового кода jQuery.

1
2
3
(function($){
 
})(jQuery);

Мы будем писать все внутри этой функции. Это позволит jQuery ждать, пока DOM не будет готов.

Так как мы заинтересованы во взаимодействии с нашим элементом card , нам нужно выбрать его.

1
2
3
(function($){
    var card = $(«.card»);
})(jQuery);

Следующим шагом является регистрация позиции курсора на элементе карты. Для этого мы будем использовать встроенное событие mousemove .

1
2
3
4
(function($){
    var card = $(«.card»);
    card.on(‘mousemove’, function (e) {});
})(jQuery);

Теперь нам нужно отследить положение курсора. Немного сложно получить правильные значения.

1
2
3
4
5
6
7
(function($){
    var card = $(«.card»);
    card.on(‘mousemove’, function (e) {
        var x = e.clientX — $(this).offset().left + $(window).scrollLeft();
        var y = e.clientY — $(this).offset().top + $(window).scrollTop();
    });
})(jQuery);

Здесь e.clientX и e.clientY возвращают позицию курсора внутри области просмотра. Однако, поскольку каждый объект card располагается относительно области просмотра, мы должны компенсировать это, извлекая значения левого и верхнего смещения.

Последнее и самое важное, что нужно учитывать, это компенсация прокрутки окна. Таким образом, поскольку ваша позиция курсора зарегистрирована относительно вашего окна просмотра, но значения смещения фиксированы, при прокрутке ваш объект становится ближе к верху или левому краю окна просмотра в зависимости от направления, в котором вы прокручивались.

В результате наше относительное расстояние до вершины или левого угла области просмотра будет меньше. Однако, поскольку значения смещения фиксированы, мы должны это компенсировать, и это делается с помощью $(window).scrollLeft() и $(window).scrollTop() . Таким образом, добавляя эти значения к соответствующим переменным, мы просто компенсируем сумму, которую мы прокручивали. В результате, когда вы наводите курсор мыши на любой из элементов вашей card , ваша позиция X будет варьироваться от 0 до ширины карты, которая определяется как 300px. Аналогично, позиция Y будет варьироваться от 0 до высоты карты, которая составляет 175px.

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

Вот краткая иллюстрация результата функции отображения.

Функция картирования
1
2
3
4
function map(x, in_min, in_max, out_min, out_max)
{
    return (x — in_min) * (out_max — out_min) / (in_max — in_min) + out_min;
}

В этой функции параметры in_min и in_max являются минимальными и максимальными значениями входного значения соответственно, которые соответствуют ширине и высоте элемента card . out_min и out_max являются минимальными и максимальными значениями, которые будут отображаться на входе.

Давайте использовать эту функцию карты с нашими позициями курсора X и Y.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
(function($){
    var card = $(«.card»);
    card.on(‘mousemove’, function (e) {
        var x = e.clientX — $(this).offset().left + $(window).scrollLeft();
        var y = e.clientY — $(this).offset().top + $(window).scrollTop();
         
        var rY = map(x, 0, $(this).width(), -17, 17);
        var rX = map(y, 0, $(this).height(), -17, 17);
    });
     
    function map(x, in_min, in_max, out_min, out_max)
    {
        return (x — in_min) * (out_max — out_min) / (in_max — in_min) + out_min;
    }
})(jQuery);

Теперь наши сопоставленные значения rX и rX .

Следующим шагом является установка правила CSS для элемента image с использованием сопоставленных значений в качестве значений поворота.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
(function($){
    var card = $(«.card»);
    card.on(‘mousemove’, function (e) {
        var x = e.clientX — $(this).offset().left + $(window).scrollLeft();
        var y = e.clientY — $(this).offset().top + $(window).scrollTop();
         
        var rY = map(x, 0, $(this).width(), -17, 17);
        var rX = map(y, 0, $(this).height(), -17, 17);
     
        $(this).children(«.image»).css(«transform», «rotateY(» + rY + «deg)» + » » + «rotateX(» + -rX + «deg)»);
    });
         
    function map(x, in_min, in_max, out_min, out_max)
    {
        return (x — in_min) * (out_max — out_min) / (in_max — in_min) + out_min;
    }
})(jQuery);

Здесь мы выбрали дочерние элементы элемента card с именем image , а затем установили правило CSS для поворота этого элемента вокруг осей X и Y на rX и rX градусов соответственно.

Вы поймете, что элементы карты следуют за курсором в их соответствующих ракурсах. Однако, когда курсор находится вне элементов карты, они сохраняют свою ориентацию. Более того, они резко реагируют на наличие курсора над элементом карты. Поэтому нам также нужно обрабатывать те случаи, когда мышь входит и покидает элемент карты.

Чтобы справиться с этими проблемами, нам нужно использовать события mouseenter и mouseleave .

Когда мышь входит в область элемента card , мы добавляем правило CSS перехода к элементу image . Это обеспечит плавный переход для «взгляда» элемента image .

1
2
3
4
5
card.on(‘mouseenter’, function () {
    $(this).children(«.image»).css({
            transition: «all » + 0.05 + «s» + » linear»
    });
});

Точно так же нам нужно обработать событие mouseleave .

Здесь я также добавляю еще одно правило CSS перехода с другой синхронизацией, которое делает плавный переход в исходное положение, когда мышь покидает элемент card .

Я также добавляю правило CSS transform для сброса вращений элемента card .

1
2
3
4
5
6
7
card.on(‘mouseleave’, function () {
    $(this).children(«.image»).css({
        transition: «all » + 0.2 + «s» + » linear»
    });
 
    $(this).children(«.image»).css(«transform», «rotateY(» + 0 + «deg)» + » » + «rotateX(» + 0 + «deg)»);
});

Итак, наш окончательный код jQuery выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(function($){
    var card = $(«.card»);
    card.on(‘mousemove’, function (e) {
        var x = e.clientX — $(this).offset().left + $(window).scrollLeft();
        var y = e.clientY — $(this).offset().top + $(window).scrollTop();
         
        var rY = map(x, 0, $(this).width(), -17, 17);
        var rX = map(y, 0, $(this).height(), -17, 17);
     
        $(this).children(«.image»).css(«transform», «rotateY(» + rY + «deg)» + » » + «rotateX(» + -rX + «deg)»);
    });
     
    card.on(‘mouseenter’, function () {
        $(this).children(«.image»).css({
            transition: «all » + 0.05 + «s» + » linear»,
        });
    });
 
    card.on(‘mouseleave’, function () {
        $(this).children(«.image»).css({
            transition: «all » + 0.2 + «s» + » linear»,
        });
 
        $(this).children(«.image»).css(«transform», «rotateY(» + 0 + «deg)» + » » + «rotateX(» + 0 + «deg)»);
    });
         
    function map(x, in_min, in_max, out_min, out_max)
    {
        return (x — in_min) * (out_max — out_min) / (in_max — in_min) + out_min;
    }
})(jQuery);

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

В этом уроке мы узнали, как использовать перспективное свойство и требуемую структуру HTML для правильной работы. Кроме того, мы рассмотрели, как зарегистрировать положение курсора мыши при наведении на определенный элемент HTML.

Кроме того, мы использовали mousemove , mouseenter и mouseleave для mouseleave интерактивности, добавляя правила CSS к элементам HTML с помощью jQuery.

Я надеюсь, что вам понравился этот урок и вы узнали некоторые полезные методы.