3D Transforms (optional)
11/14/25About 12 min
3D Transforms (optional)
NEED-TO-KNOW
- Only 2D transforms are the subject matter to be mastered for this course
- 3D transforms are NOT PART OF THE SUBJECT MATTER to be mastered for this course, but they may challenge you or inspire you for future projects
REMARKS
- The content on this page requires quite a bit of spatial insight
- In order to demonstrate some 3D properties and methods, we make use of transitions and animations
- First go through the pages on 2D Transforms and Animations before you read on
- The 2D and 3D transform methods and transform-related properties are:
| 2D and 3D | 3D only |
|---|---|
translate(unit ,[unit]),translateX(unit), translateY(unit) | translateZ(unit), translate3d(tX, tY, tZ) |
rotate(angle), rotateX(angle), rotateY(angle) | rotateZ(angle), translate3d(nr, nr, nr, angle) |
scale(nr [, nr]), scaleX(nr), scaleY(nr) | scaleZ(nr), scale3d(sX, sY, sZ) |
skew(angle), skewX(angle), skewY(angle) | |
transform-origin: unit, unit | perspective-origin: unit, unit |
| `backface-visibility: visible | hidden` |
perspective(unit) (on element itself) | |
perspective: unit (on parent element) | |
| `transform-style: flat |
translateZ() and translate3d()
translateZ(): moves an element on the Z axis- Positive arguments brings the object closer to you
- Negative arguments pushes the object further away from you
translate3d()is a shorthand fortranslateX(),translateZ()andtranslateZ()- For example:
translate3d(20px, 0, 3rem)=translateX(20px) translateY(0) translateZ(3rem)
- For example:
REMARK
translateZ()doesn't do anything at all until you add some kind of perspective- You can try this on our interactive transform page
scaleZ() and scale3d()
scaleZ(): scales an element up or down along the Z axis- Arguments larger than 1 scales the object up (zoom in)
- Arguments smaller than 1 scales the object down (zoom out)
scale3d()is a shorthand forscaleX(),scaleZ()andscaleZ()- For example:
scale3d(1, 1.5, .7)=scaleX(1) scaleY(1.5) scaleZ(.7)
- For example:
REMARK
scaleZ()doesn't do anything at all until you add some kind of perspective- You can try this on our interactive transform page
rotateZ() and rotate3d()
rotateZ(): rotates an element around the Z axisrotate3d()is specified by three numbers for the X, Y and Z axes and one angle for the rotation
REMARKS
- The
rotate3d()property is NOT a list of X, Y and Z rotations liketranslate3d()andscale3d() - The three arguments together form a matrix from which ONE axis is calculated (the details of this calculation are beyond the scope of this page) around which the fourth argument (the angle) will rotate
perspective() method
- Perspective is an important feature to get a natural sense of depth, especially when rotating over one of the axes
- The perspective defines how far your eyes are away from the object
- A larger number means that you are further away from the object and the perspective looks smaller
- A smaller number means that you are closer to the object and the perspective looks larger
- There are two ways to generate some depth:
- The
perspective()method as a value oftransformgives depth to that specific element - The
perspectiveproperty gives depth on a total scene (that is, on a group of child elements)
- The
- Remember that when we use
rotateX()androtateY()to rotate along the X and/or Y axis, it looks like the elements are squeezed because, by default, they have no depth - By adding the
perspective()method in front ofrotateX()androtateY(), you enter the 3D space for this specific element and the rotation looks more natural - In the example below:
- The left animation has no
perspective()method
#without div span { animation: withoutPerspective 4s linear infinite; } @keyframes withoutPerspective { to { transform: rotateX(0); } to { transform: rotateX(360deg); } }- The right animation has the
perspective(200px)method IN FRONT OF the rotation method
#with div span { animation: withoutPerspective 4s linear infinite; } @keyframes withPerspective { to { transform: perspective(200px) rotateX(0); } to { transform: perspective(200px) rotateX(360deg); } } - The left animation has no
Exercises
- Play with the different perspective values, e.g.
perspective(500px),perspective(100px) - Change the rotation axis from
rotateX()torotateY() - Change the
transform-originto another rotation point, for example:
#with div span {
animation: withPerspective 4s linear infinite;
transform-origin: right;
}Note that, by specifying only one value for transform-origin, the other value defaults to center
perspective property
- Sometimes you have one object that contains two or more child elements (let's call it a "scene")
- When using the
perspective()method on every child element inside the "scene", this doesn't look very natural at all ...
span {
...
animation: rotate 4s linear infinite;
transform-origin: center;
}
div:hover span {
animation-play-state: paused;
}
@keyframes rotate {
from {
transform: perspective(500px) rotateY(0);
}
to {
transform: perspective(500px) rotateY(360deg);
}
}- That's where the
perspectiveproperty comes in action: when we delete theperspective()method from the child elements and replace it with theperspectiveproperty on the parent (thedivtag) we get a more beautiful 3D perspective for the entire scene - Now the rotation on the whole scene looks very natural
div {
...
perspective: 500px; /* add perspective property for the whole scene */
}
span {
...
animation: rotate 4s linear infinite;
transform-origin: center;
}
div:hover span {
animation-play-state: paused;
}
@keyframes rotate {
from {
transform: rotateY(0); /* perspective() methode deleted */
}
to {
transform: rotateY(360deg); /* perspective() methode deleted */
}
}perspective() vs perspective
When to use the perspective() method?
- If only one element has to be transformed, use the
perspective()method within thetransformproperty of the element
When to use the perspective property?
- If two or more elements have to be transformed, use the
perspectiveproperty - First create a scene container around those elements, e.g.
div.scene, and set theperspectiveproperty on this scene - Attention: the scene itself MAY NOT have any transformation properties!
perspective-origin
- As you already know: the
transform-originproperty sets the reference point for thetransformof a child element - The
perspective-origindoes about the same, but this time for thetransform-originproperty on the whole scene - So,
perspective-origindetermines the position from which you are looking at the whole scene - The values for
perspective-originare exactly the same as fortransform-origin- The default value is also
center center
- The default value is also
- In the example below:
- All child elements are rotated
90degalong the X axis - The whole scene has a
perspectiveof300px - The animation changes the
perspective-origin - The green dot is on the
perspective-originand represents the view point from where you are looking at the perspective
- All child elements are rotated
Exercises
- Enable the
transform-originproperty and with some the different settings, e.g.top,bottom, ... - Modify the value of
rotateX()and look what happens - Rename the property
rotateX()torotateY()and look what happens
transform-style
- The
transform-styleproperty determines whether the DIRECT child elements are flattened in its 2D plane or shown in a 3D space - By default, all direct children are rendered in a flat, 2D plane (
transform-styleon parent is defaultflat) - To allow all direct children to be transformed on the Z axis (
translateZ(),rotateZ()and/orscaleZ()) , you have to EXPLICITLY set the property of the PARENT element totransform-style: preserve-3d - In the example below:
- Because we are going to transform several elements, we are going to use a scene with a global perspective
div.scene { ... perspective: 2000px; /* perspective for the whole scene */ }div.parentis the first element inside the scene and rotates along its Y axis
div.parent { ... animation: rotate 5s linear infinite; } @keyframes rotate { from { transform: rotateY(0); } to { transform: rotateY(360deg); } }- Inside
div.parentare two child elements which have a differenttranslateZ()value- The red box has to move
5remcloser to the viewertransform: translateZ(5rem); - The green box has to move
8remcloser to the viewertransform: translateZ(8rem);
- The red box has to move
span:first-child { ... transform: translateZ(5rem); } span:last-child { ... transform: translateZ(8rem); }- As you can see, the two child elements are flattened in their parent element (
div.parent) and theirtranslateZ()methods don't work!
- All you have to do to fix this behavior is enabling the 3D space on the parent element
div.parent {
...
animation: rotate 5s linear infinite;
transform-style: preserve-3d;
}Examples
3D figcaption
- Let's start from a finished transition example, in which a
figcaptionis rotated around the X axis (from90deg= invisible to0= visible) when hovering its enclosingfigure - All you have to do to change the squeezed
figcaptionto a nice, foldable version, is adding some perspective to the transformation- Add
perspective(300px)as the first method to the transformation and change the start rotation to60deg
- Add
figcaption {
...
transform: perspective(300px) rotateX(60deg);
...
}Flip card
- The flip card (
div.scene) contains two children (div.frontanddiv.back) - By default, the front and the back of the card stand beneath each other
- Step 1: Rotate the back of the card
- At the end only the front is visible in the default state and the back is visible on the hover state
- We have to rotate the back by
180degon the Y axis, to avoid that the text on the reverse side is mirrored in the hover state.back { background-color: rgba(128, 201, 240, 0.6); transform: rotateY(180deg); }
- Step 2: Flip the card
- To flip the whole card, we rotate the front and the back
180degon the hover state of the card (div.scene) - Because the back already starts from
180degonward, it continues to rotate to360deg.front, .back { ... transition: 2s; } .scene:hover .front { transform: rotateY(180deg); } .scene:hover .back { transform: rotateY(360deg); }
- To flip the whole card, we rotate the front and the back
- Step 3: Disable the backface
- Only the part of the card that is not mirrored may remain visible
- If we do not do this, we will always see the back when we put the front and the back on top of each other
.front, .back { ... transition: 2s; backface-visibility: hidden; }
- Step 4: Position the front and the back on top of each other
- Position the front and the back absolutely inside the card container
.scene { width: 200px; height: 300px; position: relative; } .front, .back { ... position: absolute; top: 0; left: 0; }
- Position the front and the back absolutely inside the card container
- Step 5: Add perspective to the scene
- All you have to do to change the squeezed rotation to a nice 3D version, is adding some perspective to the whole scene and not to the individual elements
.scene { width: 200px; height: 300px; position: relative; perspective: 500px; }
- All you have to do to change the squeezed rotation to a nice 3D version, is adding some perspective to the whole scene and not to the individual elements
- Final step: Put the text in front of the card
- Translate the text by
2remon the Z axis to place it in front of the rest of the card - By default, the Z axis is rendered inside a flat surface, that's why we have to explicitly enable 3D on all its parent elements
- The parent element for the text 'front' is
div.frontand the parent for the text 'back' isdiv.back
The parent element fordiv.frontanddiv.backisdiv.sceneh1 { margin-top: 3rem; color: firebrick; transform: translateZ(2rem); } .scene { ... perspective: 500px; transform-style: preserve-3d; } .front, .back { ... transform-style: preserve-3d; }
- Translate the text by
Flapping image
- Our scene contains two levels of nested elements, and they both contain their own animation
- The
figurerotates around the X axis - The
img, inside thefigure, "flaps" on the top of thefigure
- The
- Step 1: Add perspective to the scene
.scene { perspective: 1000px; } - Step 2: Let the image "flap"
- Add an alternating animation to the
imgtag (rotate between-40degand40deg) - Set the
transform-originto thetopof the image
img { vertical-align: middle; width: 100%; height: auto; animation: flapImage 700ms ease-in-out infinite alternate; transform-origin: top; } @keyframes flapImage { from { transform: rotateX(-40deg); } to { transform: rotateX(40deg); } } - Add an alternating animation to the
- Step 3: Rotate the figure
- Rotate the
figurearound his Y axis - Pause the animation on the hover state of the scene
.scene figure { animation: rotateFigure 10s linear infinite; } .scene:hover figure { animation-play-state: paused; } @keyframes rotateFigure { from { transform: rotateY(0); } to { transform: rotateY(360deg); } } - Rotate the
- Final step: Preserve 3D on the
figure- The image stops to flap because the figure has his own transform now!
(Remember that child elements are rendered on a flat pane by default) - Enable 3D on the
figure
figure { ... transform-style: preserve-3d; } - The image stops to flap because the figure has his own transform now!