CSS 3D animation, how?

2019-01-21 11:37发布

问题:

I would like to animate things in 3D space. I know this is possible with CSS and HTML5, but I can't find a good tutorial for practical use!

I found this website as an example. You can select text etc. All the time. Can somebody give a very easy to understand and little example how this works? I see the source code but I don't really understand it…

Is this CSS3 or HTML5 or both?

How much JavaScript do I need?

Which browsers support this?

回答1:

Its all css3 and javascript. Just inspect the site in your favorite inspector, dig into the dom, and you'll see something like

-webkit-perspective: none;
-webkit-perspective-origin: 200px 200px;
-webkit-transform: matrix3d(-0.9386958080415784, -0.197569680458564, 0.2825179663820851, 0, 0, 0.8194947008605812, 0.5730867606754029, 0, -0.34474654452969944, 0.537954139890128, -0.7692562404101148, 0, 0, 0, 0, 1);
-webkit-transform-style: preserve-3d;
-webkit-transition-delay: 0s;
-webkit-transition-duration: 16s;
-webkit-transition-property: all;
-webkit-transition-timing-function: linear;

how much javascript you need depends on how you want to implement it. It can be 'a lot' or 'a little'.

The more modern a browser, the better the change it will run well. Check here to see which browsers support what.



回答2:

There are two parts to this.

  1. The first one is about building the 3D shape.
  2. The second one involves animating it (and this is the simple part).

I'll go through each one of them in detail, but first I'll briefly answer the three questions you've asked at the end.

is this css3 or html5 or both?

It's CSS3. CSS 3D transforms and keyframe animations.

how much javascript do I need?

Well, if you don't want to create 3D shape itself with JavaScript, then you don't need any JavaScript to animate it. Any browser that supports 3D transforms also supports keyframe animations and, if a browser supports keyframe animations, then you probably want to use those instead of jQuery (and maybe even your own custom JS) animations.

which browsers support this?

Things didn't look good two years ago, but they're getting better...

✓ Chrome, Safari, Firefox (though Firefox 3D animations are jiggly and not nice to my CPU... I do have a 6-year-old old laptop, it's true) Opera 15+ (WebKit) support 3D transforms. And of course keyframe animations.

Firefox supports everything unprefixed, Chrome/Safari/Opera need the -webkit- prefix - update: Chrome 36+ and Opera 23+ support them unprefixed as well, so it's only Safari left now. I won't be using any prefixes in the answer or in the demo (-prefix-free takes care of things there). Another update: Safari 9 unprefixes transforms.

3D transforms were not supported by Opera before switching to WebKit.

✗ IE up to and including 9 don't support 3D transforms. IE10 and IE11 support 3D transforms, but do not support nesting of 3D transformed elements (cannot create realistic looking 3D shapes via applying 3D transforms on both parent and child elements, as 3D transformed children of a 3D transformed parent get flattened into the plane of the parent). Update: Edge now supports nesting of 3D transformed elements.


Alright, I hope that clears a few things. Now...

1 Building a CSS cube.

1.2 What is a cube and starting off with the HTML

First of all, try to picture a cube. Maybe this link helps? Or let's have an image here as well.

It has 6 square faces. 1 top, 1 bottom; 1 in front, 1 in the back; 1 left, 1 right. This means that the HTML is simply:

<ul class='cube'>
    <li class='face'></li>
    <li class='face'></li>
    <li class='face'></li>
    <li class='face'></li>
    <li class='face'></li>
    <li class='face'></li>
</ul>

You first put some regularity into the faces, give them equal width and height, position them absolutely so that they're all stacked one on top of the other, give them different backgrounds, they don't mind that. You can give them some padding, put some dummy text into them, whatever...

Now comes the interesting part: moving them so that they form a cube in space. You do that using 3D transforms.

1.2 The Coordinate System

Consider this 3D coordinate system:

Initially, all the 6 faces are all exactly where you see the blue square, in the xOy plane (where O is the intersection of the 3 axes).

Pay attention to the direction of the y-axis. Coming from a mathematical background, this seemed a bit weird to me at first, but this is how the coordinate system is for the screen.

In 2D, the origin O is at the top left corner, so the + (positive direction) of the x-axis points right and the + of the y-axis points down.

1.2.a Translations along the axes

So a translation of a positive value along the x-axis (for example, translateX(10px)) moves the element to which it is applied to the right (towards the + of the x-axis), while a translation of a negative value along the x-axis (something like translateX(-10px)) moves it to the left.

Similarly, a translation of a positive value along the y-axis (like translateY(10px)) moves the element down (towards the + of the y-axis), while a translation of a negative value along the y-axis (like translateY(-10px)) moves it up.

Now add another dimension. With the z-axis. The + (positive direction) of the z-axis comes out of the screen, towards you. So a translation of a positive value along the z-axis (like translateZ(10px)) moves the element forward (towards the + of the z-axis and towards you), while a translation of a negative value along the z-axis (like translateZ(-10px)) moves it backward (away from you).

1.2.b Rotations around the axes

Rotations of positive angle values (for example, rotate(15deg) - note that if the rotation axis is not specified, then it is assumed to be the z-axis) in CSS are clockwise and rotations of negative angle values (like rotate(-15deg)) are counter-clockwise.

And clockwise means clockwise as seen from the + of the axis around which you rotate the element.

So a positive rotation around the x-axis means a clockwise rotation in the yOz plane as seen from the + of the x-axis, which is at the right.

A positive rotation around the y-axis means a clockwise rotation in the zOx plane (the horizontal plane) as seen from the + of the y-axis, which is at the bottom.

A positive rotation around the z-axis means a clockwise rotation in the xOy plane (the plane of the screen) as seen from the + of the z-axis, which is how you naturally see the screen.

1.3 Put the faces in the right positions in order to form the cube

1.3.1 Put one face at the front

This means translating it forwards (in the positive direction) along the z-axis. What is this? A translateZ of a positive value. What value? Well, it should be half the width (or the height, doesn't matter, it's a square, they're equal).

Suppose I have the width: 16em;

Then in this case, you translate the face forward (along the positive z-axis) by 16em/2 = 8em. In CSS, that's

.face:nth-child(1) { transform: translateZ(8em); }

Note: The translate transform moves the entire coordinate system of the element that is translated (and consequently, the transform-origin for any subsequent transforms).

1.3.2 Put the second face at the back

That's simple, right? Just a translate along the z-axis, by the same value in the opposite direction, right? .face:nth-child(2) { transform: translateZ(-8em); }, right?

Well... actually... only if you don't want to put any content on that face. Or if you don't want to have as a background an image for which it matters which is left and which is right.

Each of these squares that make up the cube has a front and a back. The front is the one towards the positive direction of the z-axis; the one that "looks at you from the computer screen". If you put text there, it flows normally on the front. But it looks vertically mirrored on the back.

That's why the first thing that you should do is to rotate the second square face by 180° around the vertical axis (y-axis). After doing that, you can then translate this second square face along the z-axis in order to move it to the back.

The translate value is again positive in this case. Just like the translate transform moves the coordinate system of the element that is translated, the rotate transform... well... rotates it. This means that after rotateY(180deg) is applied, the + of the z-axis points towards the back (not towards the front anymore).

So the CSS that rotates and then translates the second face into its position on the cube is:

.face:nth-child(2) { transform: rotateY(180deg) translateZ(8em); }

Note: the cube is a really simple 3D shape, but one CSS property that I find really useful to check whether I've rotated faces the right way is backface-visibility. If I set it to hidden and I don't see the rotated element, it means that I'm looking at it from the back.

1.3.3 Put the third face to the right

First of all, its front has to "look" towards the right. This means that it has to be rotated around the y-axis so that the + of the z-axis ends up pointing towards the right and then it has to be translated along the positive z-axis by the same positive value (8em in this case) that is half the length of the side of the square.

Rotated by what angle? Well, 90°, which means the CSS needed is:

.face:nth-child(3) { transform: rotateY(90deg) translateZ(8em); }

1.3.4 Put the fourth face to the left

First rotate it by 90°, but the other way, to make its front "look" towards the left. "The other way" means rotateY(-90deg), then apply the same old translateZ(8em). In one CSS line:

.face:nth-child(4) { transform: rotateY(-90deg) translateZ(8em); }

1.3.5 Put the fifth face at the top

First rotate it by 90° around the x-axis and then translate along the z-axis (which points up after the rotation). CSS:

.face:nth-child(5) { transform: rotateX(90deg) translateZ(8em); }

1.3.6 Put the sixth (and last!) face at the top

Rotate it by 90° the other way around the x-axis, then translate it along the z-axis (which points down after the rotation). CSS:

.face:nth-child(6) { transform: rotateX(-90deg) translateZ(8em); }

1.4 Perspective and realistic looking 3D shapes

All the faces of the cube are now in place. But this won't look like much of a 3D shape unless the faces that are closer seem bigger than those that are further away. The CSS property that takes care of this is called perspective and is applied on the parent of the elements which have 3D transforms applied on them.

It still won't look like much of a 3D shape if you look perpendicular to the center of the front face because you won't see any of the other faces. To fix this, you need to rotate the cube itself in 3D. And this is why it's important that the browser allows nesting of 3D transformed elements.

You enable nesting of 3D transformed elements with transform-style: preserve-3d; on the parent element (.cube in this case). Unfortunately, IE10/11 do not support the preserve-3d value for the transform-style property (only support the flat value), so you cannot have a realistic looking cube in IE yet (unless you take the 3D transforms applied on the cube and chain them before the transforms applied for each face, which means one set of keyframes for each face if you want to animate the cube as a whole).

Update: transform-style: preserve-3d now landing in IE as well.

Note: If you also apply 3D transforms on the .cube, then you should move the perspective property on the parent of the .cube.

2 Animating the CSS cube

That's done using just the regular keyframe animations for the .cube element. Let's say you want to rotate it:

@keyframes ani { to { transform: rotate3d(5, 12, 13, 360deg); } }

You can have multiple keyframes, with rotations around different axes, but this is the basic idea.


TL;DR

Just give me the damn demo! Alright, here it is:

simple rotating cube demo


And a few more demos I've done recently and which show how to create other slightly more complex 3D shapes:

❆ square antiprism

❆ cuboctahedron

❆ rhombicuboctahedron

❆ rhombic dodecahedron

❆ octahedron

❆ truncated cube