In my previous tutorial I showed you how to use CSS 3D Transforms to create an isometric grid layout. It was quite challenging, since a few browsers such as Firefox have slightly different approaches in regard to how they render elements on a three-dimensional plane.
In this tutorial we’ll continue exploring 3D Transforms, by building a 3D parallax rollover effect, inspired by Apple TV’s interface. We’ll still be using Envato Elements as inspiration for our content; here’s what we’re working towards:
Hover over the thumbnail and move your cursor around; the thumbnail will lean according to the cursor position and the direction of movement.
Perspective: a Primer
As human beings our visibility is limited up to the vanishing horizon, and our binocular vision creates what we perceive as perspective. Perspective helps us interpret an object to be further away, rather than simply being smaller.
The isometric grid from the previous tutorial does not take “perspective” into account. The grid size–height and width–is actually retained. In reality we should be seeing the farthest side of the grid shrinking due to perspective.
In CSS, we can add perspective to 3D transformation through the perspective()
function. It is worth noting that perspective()
has to be added before other transform functions such as rotate()
and translate()
for it to take effect. It also requires a value defining the view distance relative to the element; whether the element should be viewed from afar or near.
#perspective { transform: perspective(600px) rotateX(60deg) rotateY(0deg) rotateZ(-45deg); }
That’s how we render a 3D plane with CSS. Now let’s see how we can apply this to create a parallax effect. We’ll start with the HTML:
The HTML Markup
If you followed our previous tutorial thoroughly, you will find the HTML markup to be quite similar. Here we have a div
with a class of ItemCard
wrapping the name, the image, and the link pointing to one of the items on Envato Elements.
<div class="ItemCard"> <a class="ItemCard__dest cover" href="https://elements.envato.com/items/type/fonts/campfire-stories-font-duo-DH6LQG" target="_blank"></a> <figure class="ItemCard__thumb"> <img src="./images/001.jpg" height="340" width="510" alt=""> </figure> <div class="ItemCard__layer cover"></div> <div class="ItemCard__summary cover"> <span class="ItemCard__meta category">Fonts</span> <h2 class="ItemCard__title">Herbert Lemuel</h2> <address class="ItemCard__meta designer">August10</address> </div> </div>
Add the above HTML markup from top to bottom.
The CSS
We begin by adding some reset styles which, in this case, set the element’s box-sizing to border-box
, remove the figure
element margin, and make the image fluid.
html { box-sizing: border-box; } *, *:before, *:after { box-sizing: inherit; } figure { margin: 0; } img { max-width: 100%; height: auto; }
With that done, we add some styles to the ItemCard
. Beyond the aesthetic rules such as border-radius
and box-shadow
, we define the element transition and transformation including the perspective()
function.
.ItemCard { position:relative; display:flex; overflow:hidden; flex-direction:column; cursor:pointer; border-radius:6px; box-shadow:0 2px 10px rgba( 0,0,0,.3 ); align-items:center; max-width: 510px; height: auto; transition: transform .5s cubic-bezier(.215, .61, .355, 1), box-shadow .5s cubic-bezier(.215, .61, .355, 1); transform: perspective( 600px ) translate3d( 0, 0, 0 ); }
For more information on what cubic-bezier()
is doing here, check out Guy Routledge’s course:
Meta Styles
Next, we add styles the ItemCard
child elements; the item name, the item author name, and the item category. Similarly, the styles define the these elements’ visibility, position, sizing, transitions, and transformations.
.ItemCard .cover { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .ItemCard__dest { z-index: 30; display: block; } .ItemCard__thumb { position: relative; z-index: 3; padding: 0; transition: transform .2s; } .ItemCard__thumb img { display: block; transition: box-shadow 0.2s; } .ItemCard__layer { z-index: 10; transition: opacity 1s cubic-bezier(0.215, 0.61, 0.355, 1); opacity: 0; background: linear-gradient(120deg, #9d50bb 0%, #4096ee 100%); } .ItemCard__summary { z-index: 20; padding: 25px; display: flex; flex-direction: column; transform: perspective(600px) translate3d(0, 100%, 0); text-align: center; color: #fff; justify-content: center; } .ItemCard__title { margin: 8px 0; font-weight: 900; transform: translate3d(0, 150px, 50px); text-transform: uppercase; color: #fff; font-size: 19px; line-height: 1.48; }
3D Rendering
Add the following styles to make the child elements of the ItemCard
and the .ItemCard__summary
compliant with three-dimensional rendering.
/** * Preserve 3D elements for the descendants */ .ItemCard, .ItemCard__summary { transform-style: preserve-3d; backface-visibility: hidden; }
Hover
The ItemCard
hover state styles define the box-shadow
as well the Z
axis of some of the child elements:
/** * Hover States */ .ItemCard:hover { box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3); } .ItemCard:hover .ItemCard__layer { opacity: .9; } .ItemCard:hover .ItemCard__title { transform: translate3d(0, 0, 50px); } .ItemCard:hover .ItemCard__meta.category { transform: translate3d(0, 0, 40px); } .ItemCard:hover .ItemCard__meta.designer { transition-delay: .05s; transform: translate3d(0, 0, 20px); }
At this point, you’ll have something which resembles the following:

Parallax Effect With jQuery
We’re going to harness some jQuery here, so make sure you’re linking to jQuery somewhere from within your document. In CodePen add https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js as an external JavaScript path.

The parallax effect is applied through two jQuery Events mousemove
and mouseout
. The mousemove
event fires up when the cursor is moving over the target element; in this case,.ItemCard
. The mouseout
fires when the mouse cursor is already outside the target element.
$( document ) .on( "mousemove", ".ItemCard", function( event ) { } ) .on( "mouseout", ".ItemCard", function() { } );
During the mousemove
event, we retrieve the mouse cursor coordinates, determine the ItemCard
rotation, and apply these numbers by adding inline styles. During the mouseout
event, we strip out the inline style so that the ItemCard
returns to its initial position.
$( document ) .on( "mousemove", ".ItemCard", function( event ) { /** * Half of the `ItemCard` width * @type {integer} */ var halfW = ( this.clientWidth / 2 ); /** * Half of the `ItemCard` height * @type {integer} */ var halfH = ( this.clientHeight / 2 ); /** * Mouse cursor X coordinate * @type {integer} */ var coorX = ( halfW - ( event.pageX - this.offsetLeft ) ); /** * Mouse cursor Y coordinate * @type {integer} */ var coorY = ( halfH - ( event.pageY - this.offsetTop ) ); /** * X Rotation degree of `ItemCard` * @type {integer} */ var degX = ( ( coorY / halfH ) * 10 ) + 'deg'; // max. degree = 10 /** * Y Rotation degree of `ItemCard` * @type {integer} */ var degY = ( ( coorX / halfW ) * -10 ) + 'deg'; // max. degree = 10 /** * Add the inline styles */ $( this ).css( 'transform', function() { return 'perspective( 600px ) translate3d( 0, -2px, 0 ) scale(1.1) rotateX('+ degX +') rotateY('+ degY +')'; } ) .children( '.ItemCard__summary' ) .css( 'transform', function() { return 'perspective( 600px ) translate3d( 0, 0, 0 ) rotateX('+ degX +') rotateY('+ degY +')'; } ); } ) .on( "mouseout", ".ItemCard", function() { /** * Remove the inline styles */ $( this ).removeAttr( 'style' ) .children( '.ItemCard__summary' ) .removeAttr( 'style' ); } );
And that’s a wrap.
Wrapping Up
In this tutorial we learned how to leverage 3D transformations to build a 3D parallax effect inspired by Apple TV.
Bear in mind, this effect will not work well on touch-enabled devices, so you’ll need to consider some kind of fallback, possibly with Touch Events to replicate the parallax effect–I will leave that on the table for you to tackle!
Useful Resources
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post