Logo

Front End Development Tutorials, Tips, and Tricks


CSS Transitions, Transforms & Animations – Perspective

30th December, 2014

In this part of the CSS transitions, transforms and animations series, we look at the CSS perspective property with examples including a 3D cube.

CSS Transitions, Transforms & Animations – Perspective

In this tutorial, we're going to take an in-depth look into CSS perspective, and some use-cases where it can give users more natural and cool experiences.

What Is Perspective?

In the context of the two-dimensional world, perspective gives users the illusion of an object’s depth, width, height, and position in relation to another object, when viewed from a particular point. Perspective allows artists to create scenes that look like they are going “into” the paper, or draw buildings of different sizes on a street moving away from our position. Objects will also have a vanishing point, which is the point in the 2-D plane where an object’s depth vanishes. Let’s illustrate this with a simple diagram:

Vanishing Point

The above diagram depicts three objects, all rectangles, in a 2-D plane. When they are moved around the plane, lines of perspective can be drawn towards the vanishing point, and 3-dimensional objects can be extrapolated.

Since the web is viewed on two-dimensional surfaces (i.e. device screens), perspective can play a part in helping to create some similar depth. In the world of web and CSS transforms, perspective is defined like this (taken from the MDN):

The perspective CSS property determines the distance between the z = 0 plane and the user in order to give to the 3D-positioned element some perspective. Each 3D element with z > 0 becomes larger; each 3D-element with z < 0 becomes smaller. The strength of the effect is determined by the value of this property.

The vanishing point is placed by default at the centre of the element, but we can reposition it with the perspective-origin property. Let’s look at a basic example.

Demo 1 – A Basic Perspective Example

Let’s take a very primitive shape – a square – and use the perspective properties to give it a bit of depth. For the first sequence of demos, I’ll use some helper classes to show-off the following perspective ranges:

  • no perspective
  • 100px perspective
  • 200px perspective
  • perspective-origin at [0, 0]

Note that perspective properties must exist on the parent container of the element we want affected. With that in mind, the markup for the three demos will be really simple:

<div class="perspective-container perspective--none">
  <span class="surface"></span>
</div>

<div class="perspective-container perspective--200">
  <span class="surface"></span>
</div>

<div class="perspective-container perspective--100 perspective-origin--00">
  <span class="surface"></span>
</div>

Let’s now apply the necessary CSS perspective properties:

.perspective-container {
  width: 200px;
  height: 200px;
  background-color: #eee;
}

.perspective--none {
}

.perspective--100 {
  perspective: 100px;
}

.perspective--200 {
  perspective: 200px;
}

.perspective-origin--00 {
  perspective-origin: 0px 0px;
}

.surface {
  display: block;
  width: 100%;
  height: 100%;
  background-color: #377CFF;
}

If we refresh our browser, we will see three identical squares. Why? Because none of our objects have actually moved in the perspective plane. To see how the perspective properties affect our objects, let’s translate them along the z-axis into the plane using transform. We’ll add the following CSS to our .surface class that governs our objects:

.surface {
  transform: translateZ(-50px);
}

Now, we can see three different results:

  1. The first remains unchanged, because without any perspective rules in place, translating along the z-axis (i.e. in and out of the screen) causes no change in depth.
  2. The second appears to have moved into the screen a bit, because we set the perspective to 200px. Remember from the definition above that the perspective property determines the distance between the z = 0 plane and the user.
  3. The third appears even smaller still, and has moved to the top-left corner of our perspective container. This is because of the perspective-origin property set on the parent container.

Here it is in action:

Demo 2 – Rotating Shapes With Perspective

At this point, the above effects could’ve just been achieved by simply scaling the elements. That’s a fair point, but in that case, we’d lose all perspective properties. Visualizing the perspective in the first demo might also be a bit difficult because ultimately, it’s just a smaller square. Let’s look at a slightly more advanced implementation, and paint a clearer picture.

This time, we’ll have the same three squares, but we will rotate them about the y-axis, making them rotate “into” the screen. We will use a CSS transition effect coupled with the :hover pseudo-class to animate how perspective properties affects the rotation. Here’s the markup:

<div class="perspective-container perspective--none">
  <span class="surface"></span>
</div>

<div class="perspective-container perspective--400">
  <span class="surface"></span>
</div>

<div class="perspective-container perspective--400 perspective-origin--00">
  <span class="surface"></span>
</div>

This time, I added a perspective helper class at 400px also like this:

.perspective--400 {
  perspective: 400px;
}

Finally, let’s take a look at the CSS for our new shape:

.surface {
  display: block;
  width: 100%;
  height: 100%;
  background-color: #377CFF;
  transition: transform 1s;
}

.perspective-container:hover > .surface {
  transform: rotateY(180deg);
}

Hovering over the three different implementations results in three different outcomes:

  1. With no perspective set, the object rotates about the z-axis in a very flat manner. Nothing seems to pop in our out of the screen.
  2. When we set perspective to 400px, we can see that the object now looks like it is spinning out and back into the screen. This is because of perspective.
  3. With the perspective set to 400px and the perspective-origin moved to [0 0], a similar effect to number 2 is achieved, but this time viewed from the perspective origin.

Here it is in action! Now, we’re starting to see the power of CSS perspectives.

Demo 3 – 3D Cube With Variable Perspective Origins

Now, we’re going to get a little funky. We’re going to create a cube (with 6 faces) using CSS transform, and give that cube some depth using perspective. We will also set up a controller system so that we can edit the perspective and perspective origin on the fly, giving us a superior grasp on how perspective really affects everything.

We’ll set up our cube using 6 different span elements inside a parent div with the class of cube-face. Each cube-face will represent a different face of the cube. The cube itself will now reside inside a perspective div, called cube. Here’s the markup:

<div id="cube" class="cube">
  <div class="cube-faces">
    <span class="cube-face cube-face--front"></span>
    <span class="cube-face cube-face--back"></span>
    <span class="cube-face cube-face--top"></span>
    <span class="cube-face cube-face--bottom"></span>
    <span class="cube-face cube-face--left"></span>
    <span class="cube-face cube-face--right"></span>
  </div>
</div>

We’ll set up some defaults for the perspective wrapping container, and the cube container will have relative positioning so that each of the cube faces can be absolutely positioned inside, and transformed accordingly. Assuming we want a cube where each side is 200px in length, our CSS will look like this:

.cube {
  perspective: 1000px;
  perspective-origin: 0px 0px;
}

.cube-faces {
  position: relative;
  margin: 100px auto;
  width: 200px;
  height: 200px;
  transform-style: preserve-3d;
}

Notice that I initially set the perspective to be 1000px, and the perspective-origin to be 0px 0px. This will give us some depth to work with initially so we can see transformed objects moving into and out of the plane. Remember, without perspective, transformed objects would not achieve that third-dimensional depth that we’re after.

Let’s examine the faces of the cube now. First of all, each face needs to be absolutely positioned and have a width and height of 200px. I’ll apply some inner shadows to each face also, for visualisation purposes. Here’s the common CSS for the faces:

.cube-face {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 200px;
  height: 200px;
  background-color: rgba(#377CFF, 0.3);
  box-shadow: inset 0 0 0 2px #fff;
}

Now, let’s look at each individual face. We set up our cube to be 200 x 200 x 200. But we want to give some depth in general. So let’s pull the front face towards us by 100px and push the back face away from us by 100px, leaving us with that perfect 200px square as the center plane of our cube on the x-y axis. The back face should also be rotated 180deg, so that it is facing outwards. So we’re translating each face first, then rotating where necessary. Here’s the CSS:

.cube-face--front {
  transform: translateZ(100px);
}

.cube-face--back {
  transform: translateZ(-100px) rotateY(-180deg);
}

Now, let’s look at the left and right faces. The left face needs to be translated to the left by 100px, then rotated by –90 degrees about the y-axis. The right face does the opposite. These rules will get them into position:

.cube-face--left {
  transform: translateX(-100px) rotateY(-90deg);
}

.cube-face--right {
  transform: translateX(100px) rotateY(90deg);
}

Now, let’s take a look at the top and bottom faces. The top face needs to move upwards 100px, and rotate about the x-axis by 90 degrees, while the bottom needs to to the opposite. These rules place them in position:

.cube-face--top {
  transform: translateY(-100px) rotateX(90deg);
}

.cube-face--bottom {
  transform: translateY(100px) rotateX(-90deg);
}

And voila, now we have our CSS cube, using transforms and perspective! Let’s take it a step further though add some controls now that will allow us to change perspective and perspective-origin on the fly. I’ll use the range input, and wire up three of them to handle the respective properties. Here’s the markup:

<input id="p" type="range" min="500" max="5000" value="1000" step="1">
<input id="pX" type="range" min="-5000" max="5000" value="0" step="1">
<input id="pY" type="range" min="-5000" max="5000" value="0" step="1">

And here’s some JavaScript to give us a playful little demo:

var cube = document.getElementById('cube');
var p = document.getElementById('p');
var pX = document.getElementById('pX');
var pY = document.getElementById('pY');
var pVal;
var xVal;
var yVal;

p.addEventListener( 'input', function() {
  cube.style.perspective = `${p.value}px`;
});

pX.addEventListener( 'input', function() {
  cube.style.perspectiveOrigin = `${pX.value}px ${pY.value}px`;
});

pY.addEventListener( 'input', function() {
  cube.style.perspectiveOrigin = `${pX.value}px ${pY.value}px`;
});

And now, we have an interactive demo of a 3D cube, creating using CSS3 transforms and perspectives. The demo should truly give you a grasp on how perspective is handled in the browser. Check it out here below!

Browser Support

Perspective, transitions, and animations are supported in IE10 and up, whereas transforms are supported in IE9 and up. Fallbacks should be easy to implement as a result of this. I tested all the code above in new versions of Chrome, Safari, and Firefox, so you’re safe there. Make sure to add all then necessary vendor prefixes though!

Wrap Up

And that’s a wrap! We’ve just taken an in depth look at CSS3 perspective, and some of the possibilities available when combining perspective with transforms, transitions, and animations. Thanks again for reading, and if you have and questions, comments, or feedback, feel free to get in touch.


More Posts…

CSS Sticky Footer with Flexbox and Grid

CSS Sticky Footer with Flexbox and Grid

8th April, 2019

In this quick tutorial, we'll build a sticky footer layout two ways — using CSS Flexbox and Grid.

CSS Only Floated Labels with :placeholder-shown pseudo class

CSS Only Floated Labels with :placeholder-shown pseudo class

15th September, 2018

In this tutorial, we’re going to build a CSS only solution to the floated label technique using the :placeholder-shown pseudo class.

SVG Vector Effects

SVG Vector Effects

30th June, 2016

In this tutorial, we take a look at a lesser known vector effect SVG attribute that helps us scale SVGs without scaling their strokes.


dev.callmenick.com