In this tutorial, we will create an interactive element with the CSS3 perspective property in order to give a sense of three dimensions. This tutorial will also teach you how to use jQuery with mouse events in order to obtain element positions in JavaScript and how to manipulate the CSS properties.
The HTML Structure for the Perspective Property
We need a parent-child relation for the perspective property to work properly. Let’s first create the HTML structure and then continue with the CSS styling.
Mountain
5 Days
Island
2 Days
Here we are wrapping two card
elements into a div with the id of cardsWrapper
. Also, this cardsWrapper
is wrapped into another div in order to be able to manipulate its position within the viewport easily.
Each element with the class of card
has an image
element, which embraces the screen
and text
elements. The structure is a bit vague for the time being, but I will explain the use of each element in the related upcoming sections.
Let’s use the following CSS styling in order to position our elements.
#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; }
Using the Perspective Property
The perspective
property is something that you have to set in your parent div which contains the divs that you want to transform with the sense of perspective. Imagine the parent div is your world, and it has a certain perspective value that you are experiencing.
Let’s add the perspective
property to our parent div, which is card
. We are selecting the card
element as the parent, not cardsWrapper
, since we want to have an individual perspective effect on each card element.
Tweak the CSS of the card
as follows.
.card{ width: 300px; height: 175px; background-color: red; perspective: 500px; }
Now try to add a transform
property to the image element to see the effect of the perspective.
.image{ width: 100%; height: 100%; background-color: red; transform: rotateX(30deg); }
Since image
is the direct child of card
, it is affected by the perspective. However, if you try to add a transform
property to any child of the image
, this would not work.
In order for these children to be transformed relative to their parent, which is the image
element in this example, you should use transform-style: preserve-3d
on the parent element.
.image{ width: 100%; height: 100%; transform-style: preserve-3d; transform: rotateX(30deg); }
Now we have a sufficient background in the perspective property and are ready to continue with styling the other elements.
Don’t forget to remove the transform: rotateX(30deg)
from your image element.
Styling the Cards
We have an image
element, and on top of it we have an element called screen
, and then the text
. Since we are using perspective here, you can think of each of these elements as individual layers.
Now we will add a background-image to our image
div and then style the screen
and text
elements.
Let’s use the following CSS styling to add a background-image to each single card object.
.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"); }
Now we will style the screen
element.
Since we want the screen
element to be exactly the same size as its parent image
element, we use 100% width and height values and a grey black background-color with an alpha channel.
The import part is transform: translateZ(30px) scale(0.940)
.
So here we just translate the screen
element on the Z-axis by 20px. This makes it hover over the image
element. Since it is towards us, it will be larger in size, due to the rules of perspective. Therefore, we scale it down to match the size with the parent element. If you use different translation values, the scale value would vary. Likewise, defining different height and width sizes for the parent element would result in a requirement of a different scaling value.
.screen{ background-color: rgba(0, 0, 0, 0.22); width: 100%; height: 100%; transform: translateZ(30px) scale(0.940); }
In order to understand what’s happening here, just rotate your image
element around the X and Y axes by adding the following line into your CSS rule:
transform: rotateX(30deg) rotateY(30deg)
Now the last part for this section is styling the text
element, which is quite trivial.
We are basically using the same transform settings for the text
element in order to have it on the same level as the screen
element. The rest of the CSS is just simple styling. You can tweak it the way you like.
.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; }
This is the final result with manual rotation to see the effect.
Before proceeding further, delete the rotation rules from your CSS, since we will control the rotation automatically in accordance with the cursor position.
Now we will write some jQuery code in order to make those cards interactive.
Let’s get to it!
Adding Interaction With jQuery
Let’s start with the base jQuery code.
(function($){ })(jQuery);
We will be writing everything inside this function. This will allow jQuery to wait until the DOM is ready.
Since we are interested in interacting with our card
element, we need to select it.
(function($){ var card = $(".card"); })(jQuery);
The next step is registering the cursor position on the card element. In order to do so, we will use the built-in mousemove
event.
(function($){ var card = $(".card"); card.on('mousemove', function (e) {}); })(jQuery);
Now we need to track the cursor position. It’s a bit tricky to get the correct values.
(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);
Here, e.clientX
and e.clientY
return the cursor position inside the viewport. However, since each card
object is positioned relative to the viewport, we need to compensate for this by extracting the left and top offset values.
The last and the most important thing to consider is compensation for window scrolling. So, since your cursor position is registered relative to your viewport but the offset values are fixed, when you scroll, your object gets closer to the top or left of the viewport depending on the direction in which you scrolled.
As a result, our relative distance to the top or left of the viewport would be smaller. However, since the offset values are fixed, we need to compensate for this, and this is done by the $(window).scrollLeft()
and $(window).scrollTop()
. So, by adding those values to the respective variables, we just compensate for the amount we scrolled. As a result, when you hover over any of your card
elements, your X position would range from 0 to the width of the card, which is defined as 300px. Likewise, the Y position would range from 0 to the height of the card, which is 175px.
The next step is mapping the cursor position to a new range, which will be the amount of rotation we want to apply in units of degree, so that when your cursor stands in the middle of the card element, it would just look flat, but when you move to the left/right or top/bottom, you would get a rotation effect as if the card is following the cursor.
Here is a quick illustration for the result of the mapping function.
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 this function, the in_min
and in_max
parameters are the minimum and maximum values of the input value respectively, which correspond to the width and height of the card
element. out_min
and out_max
are the minimum and maximum values that the input will be mapped.
Let’s use this map function with our X and Y cursor positions.
(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);
Now our mapped values are rX
and rY
.
The next step is setting a CSS rule for the image
element by using the mapped values as rotation values.
(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);
Here we selected the children of the card
element which are named image
, and then set the CSS rule to rotate this element around the X and Y axes by rX
and rY
degrees respectively.
You will realise that the card elements follow the cursor in their respective perspectives. However, when the cursor is off the card elements, they retain their orientations. Moreover, they react to the presence of the cursor over the card element abruptly. So we also need to handle those cases when the mouse enters and leaves the card element.
In order to handle these problems, we need to use the mouseenter
and mouseleave
events.
When the mouse enters the region of the card
element, we add a transition CSS rule to the image
element. This will enable a smooth transition for the “gaze” of the image
element.
card.on('mouseenter', function () { $(this).children(".image").css({ transition: "all " + 0.05 + "s" + " linear" }); });
Likewise, we need to handle the mouseleave
event.
Here, I also add another transition CSS rule with a different timing, which makes a smoother transition to the initial position when the mouse leaves the card
element.
I also add the transform CSS rule in order to reset the rotations of the card
element.
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)"); });
So our final jQuery code looks like this:
(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);
Here is the final result. I also used a different sans-serif font on the text
element to make it look better.
Conclusion
Throughout this tutorial, we learned how to use the perspective property and the required HTML structure to make it work properly. Moreover, we covered how to register the mouse cursor position when hovered over a specific HTML element.
On top of that, we used the mousemove
, mouseenter
, and mouseleave
events to introduce interactivity by adding CSS rules to HTML elements with jQuery.
I hope you enjoyed this tutorial and learned some useful methods.
Powered by WPeMatico