A Deeper Look at 3D in CSS

If you read the first article in this series and I suggest you do, you would have noticed how I was using Flexbox to lay the div elements out along an axis in a container and then used justify-content with space-between to spread the elements out within the container. The container was set to 90vw (view width) and each item was set to 12vw. This allows for 6 elements to be nicely spaced out along an axis to illustrate how rotate() and translate() can be used to make a cube.  

Later in that article, I used absolute to then override Flexbox's natural function of laying out the items one after another along an axis and the cube was made.
 
This feature of Flexbox is one of its powers. We waited for the ability to lay items out along an axis for a long time but for making a 3D cube or any shape for that matter we don't really need to use it.

Change the Wrapper and Faces sizes

In the first article, I centred the cube by making all the child items absolute in the flexbox.

Even though it was a nice way to illustrate how items are transformed in relation to each other, as you could see the faces being transformed side by side, it is in fact not necessary.

To make the cube more usable we are better to make the wrapper the size you want and then make the items 100% of the wrapper.

Let's change our code to reflect those changes.

Here is the HTML.

<div class="canvas">

  <div class="cube cube_1 ">
    <div class="transform--inner rotateY45">rotateY 45</div>
    <div class="transform--inner rotateY135">rotateY 135</div>
    <div class="transform--inner rotateY225">rotateY 225</div>
    <div class="transform--inner rotateY315">rotateY 315</div>
    <div class="transform--inner rotateX90">rotateX 90</div>
    <div class="transform--inner rotateX270">rotateX 180</div>
  </div>

 </div>

And the CSS.

/* 3d Transform/Perspectives
 * Thinking in 3D
 */
.cube {
  position: absolute;
  /* Setting up the 3D space*/
  perspective: 100cm;
  transform-style: preserve-3d;
  /* Moving and rotating cube can be overridden by separate cubes */
  transform:  rotateX(-10deg);
}
.transform--inner {
  position: absolute;
  width: 100%;
  height: 100%;
  background-color: #1c5ffe;
  opacity: 0.7;
}
/* Cube 1 */
.cube_1 {
  width: var(--face);
  height: var(--face);
  transform: rotateX(-30deg) translate3d(0, 0, 0);
}
.rotateY45 {
  background-color: #fe3136;
  transform: rotateY(45deg) translateZ(var(--face-tz));
}
.rotateY135 {
  background-color: #6bfe79;
  transform: rotateY(135deg) translateZ(var(--face-tz));
}
.rotateY225 {
  background-color: #1c5ffe;
  transform: rotateY(225deg) translateZ(var(--face-tz));
}
.rotateY315 {
  background-color: #fcfe79;
  transform: rotateY(315deg) translateZ(var(--face-tz));
}
.rotateX90 {
  background-color: #d549f5;
  transform: rotateX(90deg) rotateZ(45deg) translateZ(var(--face-tz));
}
.rotateX270 {
  background-color: #ff6e00;
  transform: rotateX(-90deg) rotateZ(45deg) translateZ(var(--face-tz));
}

This what the above HTML markup and the CSS creates, you can change the var(--custom-property) values to easily modify the size of the cube.

rotateY 45
rotateY 135
rotateY 225
rotateY 315
rotateX 90
rotateX 180

Okay now we have a cube and we can adjust the wrapper size and value of the translate to be 50% of the face the size of all the cube faces also adjust.

Image
3 CSS cubes in a row

So this is nice but now let's say we want to add a whole lot of 3D objects and place them on the page how would we do that?

Positioning elements in CSS

There are 2 ways to position elements in CSS, well really only one way using slightly different tools and that is place the items in a wrapper with the position property set to relative on the wrapper and then on the items use position: absolute.

Once you have items in a wrapper set to absolute you can then position them using the left, right, top, and bottom properties with a fixed value such as px or a relative value such as percent. This works and is possibly the most common way to draw in and position in CSS.

Image
basic CSS positioning with relative and absolute elements

Related: CSS positioning using position property

The second way I am going to suggest is maybe a better way and that is to use flexbox to "centre" the items1 and then position them absolutely to centre them. This makes everything centred as shown in the following example with the crosshair. Then you can use transform: translate3D() to move items from the centre point. This may be a debatable way for positioning elements in 2D but if you want to create 3D drawings and illustrations then I propose it is the best way.

Image
positioning in CSS 3D space with transfrom property

1 When using flexjustify-content:center you are actually centring all items in the flex container, however, they are still placed next to each other along an axis.

Moving around the 3D Space

To move around the 3D space you need to now understand that the centre is 0,0,0 for the XYZ axes. To move something left or up you need to use a negative number on the X-axis and Y-axis respectively. Moreover, to move something closer to you on the Z-axis you also need to use a negative value. Remember to work in 3D space you need to have the perspective set and 3D preserved on the wrapper element.

perspective: 100cm;
transform-style: preserve-3d;

To move items you use transform: translate X Y Z or 3D.
Translate works on the axis perpendicular to the face as described in how a cube is made. But let's have another quick look here.

Image
translate on the z-axis

We can also rotate things. But also remember that the order you rotate and translate matters.

Creating a feel of 3D

I have found that setting perspective to 100cm is good but if you want to show more depth in a scene using 10cm will really start to show the perspective. I have written more about this in the upcoming Creating a Landscape part of this series but for a quick primer on perspective have a look at this image.

Image
CSS 3D perspective 10cm with cross section on of the screen with viewer
Image
CSS 3D perspective set at 100cm or 4000px

The perspective sets up the position of the 3D Z plane as seen from the viewer's screen. When you move something on the Z-axis or translate it you move it from this plane. Therefore if you set the perspective at 10cm and then translate 10cm you will no longer see the item or if the items face is longer than 10cm then it will look like it goes off the screen.

You'll notice I am using cm but you can use any measurement value you like.
I like to use cm in this context because it is kind of an arbitrary number in that it could infact be infinite. I find 900cm is flat, 100cm give some feeling of 3D but 10cm really distorts. Look the number compared to px.

96px = 72pt = 2.54cm or 1inch
source: https://www.w3.org/TR/css3-values/#absolute-lengths

That gives us:
2.5cm = ~ 100px
10cm = 400px
100cm = 4000px

On a side note since perpective is a measurement on the Z axis and it is somewhat undetermined and as stated could be infinite, we can't use % on the translateZ axis.

In this article, we looked at creating reuse-able cubes that can be modified in size by adjusting the wrapper. We also looked at re-using elements and positioning them in a 3D space. In the next part, I will take a look at creating some different basic 3D shapes.

Tags