📜 ⬆️ ⬇️

Clouds on CSS3 3D Transformations

An example of how to draw beautiful 3D clouds using CSS 3D Transformations



Accidentally I came across another interesting article about interesting effects today and decided that my favorite Habra audience might also be interested in this example. My English is the same as Azarov's Ukrainian, so the translation will be author's.

For the impatient: What you get in the end

Introduction


In this article, the author tried to tell and show how to create beautiful 3D clouds using CSS3 Transformations. The author also believes that having a basic understanding of how these very CSS 3 Transformations work will not be superfluous. Tutorial link here
')
This tutorial is divided into several simple steps. Each step is based on the code from the previous one and contains links to examples.

Do it yourself!



Pooooehaali!



1. Create a world and add a camera.

First we need to create two div elements: a viewpoint (camera) and a world. All other elements we will create dynamically using javascript.

The viewpoint covers the entire scene and plays the role of the camera. Since cameras for CSS 3D Transformations do not exist, we will imagine that the camera is the glass through which we see the world created by us. The clouds we will create inside our world with blackjack and ....

The world is a div block that is inside the camera block and in which the clouds will be located.

This is an example of HTML markup that we should get.

<div id="viewport" > <div id="world" ></div> </div> 


And add styles to our world and camera. It is very important to place the block containing our scene (world) in the middle inside the camera, otherwise the entire scene will be drawn with an offset.

 #viewport { bottom: 0; left: 0; overflow: hidden; perspective: 400; position: absolute; right: 0; top: 0; } #world { height: 512px; left: 50%; margin-left: -256px; margin-top: -256px; position: absolute; top: 50%; transform-style: preserve-3d; width: 512px; } 


And a little bit of code: we initialize our objects, hook onto the mousemove event, and create an updateView () method

 /*   world  viewport  DOM  worldXAngle  worldYAngle   ,       d ,         */ var world = document.getElementById( 'world' ), viewport = document.getElementById( 'viewport' ), worldXAngle = 0, worldYAngle = 0, d = 0; /*         ,       -180  180 ,    */ window.addEventListener( 'mousemove', function( e ) { worldYAngle = -( .5 - ( e.clientX / window.innerWidth ) ) * 180; worldXAngle = ( .5 - ( e.clientY / window.innerHeight ) ) * 180; updateView(); } ); /* ,          d */ window.addEventListener( 'mousewheel', onContainerMouseWheel ); window.addEventListener( 'DOMMouseScroll', onContainerMouseWheel ); function onContainerMouseWheel( event ) { event = event ? event : window.event; d = d - ( event.detail ? event.detail * -5 : event.wheelDelta / 8 ); updateView(); } /*  CSS3  transform   Changes the transform property of world to be  Z    d   X  worldXAngle     Y  worldYAngle  */ function updateView() { world.style.transform = 'translateZ( ' + d + 'px ) \ rotateX( ' + worldXAngle + 'deg) \ rotateY( ' + worldYAngle + 'deg)'; } 


Our world will be red. And the camera will have a gradient simulating the firmament.
So we will follow the movements of the mouse and change the viewing angles and distance between the camera and the world depending on the position of the mouse itself.
% username% move the mouse and see how the world changes its orientation in space.

Example

2. Add objects to the world

Now we need to add div-blocks inside which our clouds will be drawn. Each new unit is a new cloud. We will create absolutely positioned blocks relative to the world, but using CSS3 Transformations instead of left and top.
Initially, all these objects will be located in the center of the world. The size of these objects does not matter, as it is only wrappers for clouds. And it would be better to place them in the center of the world (set margin-left and margin-top to minus half the height and width).

 .cloudBase { height: 20px; left: 256px; margin-left: -10px; margin-top: -10px; position: absolute; top: 256px; width: 20px; } 


And we add the createCloud () function, which will create a wrapper for the cloud and add it to the DOM and the createCloud () function, which will actually generate several clouds.
We will also add a variable p, which is responsible for the perspective of the camera (a property that makes objects that are farther from the camera look smaller in size)

  d = 0, p = 400, //  worldXAngle = 0, worldYAngle = 0; viewport.style.webkitPerspective = p; viewport.style.MozPerspective = p; viewport.style.oPerspective = p; /* objects      layers     (  a  ) */ var objects = [], layers = []; /*        */ function generate() { objects = []; layers = []; if ( world.hasChildNodes() ) { while ( world.childNodes.length >= 1 ) { world.removeChild( world.firstChild ); } } for( var j = 0; j <; 5; j++ ) { objects.push( createCloud() ); } } /*  placeholder              -256  256  */ function createCloud() { var div = document.createElement( 'div' ); div.className = 'cloudBase'; var x = 256 - ( Math.random() * 512 ); var y = 256 - ( Math.random() * 512 ); var z = 256 - ( Math.random() * 512 ); var t = 'translateX( ' + x + 'px ) translateY( ' + y + 'px ) translateZ( ' + z + 'px )'; div.style.webkitTransform = t; div.style.MozTransform = t; div.style.oTransform = t; world.appendChild( div ); return div; } 


.cloudBase are simple pink rectangles marking cloud containers.

Example tynts

3. Add sprites to the clouds.

The farther into the forest, the more interesting! We will add some absolutely positioned .cloudLayer blocks that are sprites in each of the clouds.

 .cloudLayer { height: 256px; left: 50%; margin-left: -128px; margin-top: -128px; position: absolute; top: 50%; width: 256px; } 


Let's slightly change our generateCloud () function, now it also generates sprites inside the clouds.

 function createCloud() { var div = document.createElement( 'div' ); div.className = 'cloudBase'; var x = 256 - ( Math.random() * 512 ); var y = 256 - ( Math.random() * 512 ); var z = 256 - ( Math.random() * 512 ); var t = 'translateX( ' + x + 'px ) translateY( ' + y + 'px ) translateZ( ' + z + 'px )'; div.style.webkitTransform = t; div.style.MozTransform = t; div.style.oTransform = t; world.appendChild( div ); for( var j = 0; j < 5 + Math.round( Math.random() * 10 ); j++ ) { var cloud = document.createElement( 'div' ); cloud.className = 'cloudLayer'; var x = 256 - ( Math.random() * 512 ); var y = 256 - ( Math.random() * 512 ); var z = 100 - ( Math.random() * 200 ); var a = Math.random() * 360; var s = .25 + Math.random(); x *= .2; y *= .2; cloud.data = { x: x, y: y, z: z, a: a, s: s }; var t = 'translateX( ' + x + 'px ) translateY( ' + y + 'px ) translateZ( ' + z + 'px ) rotateZ( ' + a + 'deg ) scale( ' + s + ' )'; cloud.style.webkitTransform = t; cloud.style.MozTransform = t; cloud.style.oTransform = t; div.appendChild( cloud ); layers.push( cloud ); } return div; } 


Example

4. Magic

Finally we will do the magic! We have an array of layers [] that contains a link to every sprite in our world. We also have worldXAngle and worldYAngle. Now we will make it so that the perpendicular dropped to the surface of the camera, was also perpendicular to any of our sprites. In other words, sprites will always face us.

Once again we will edit the update function ()
 function update (){ for( var j = 0; j < layers.length; j++ ) { var layer = layers[ j ]; layer.data.a += layer.data.speed; var t = 'translateX( ' + layer.data.x + 'px ) translateY( ' + layer.data.y + 'px ) translateZ( ' + layer.data.z + 'px ) rotateY( ' + ( - worldYAngle ) + 'deg ) rotateX( ' + ( - worldXAngle ) + 'deg ) scale( ' + layer.data.s + ')'; layer.style.webkitTransform = t; layer.style.MozTransform = t; layer.style.oTransform = t; } updateView() ; } 


Example

Prologue

That's all, the main work is almost done. It remains only to add textures to our sprites and tweak a little CSS (remove backgrounds, frames, etc.)
Textures can be chosen absolutely for every taste: clouds, thunder clouds, toasters ... If you use different textures, you can achieve interesting effects!

Actually an example to this step and the final cleaned version

You can add sprites in any order, and you can create clouds in the shape of sheep and shepherds. The scope for fantasy is not limited.

The author of this lesson hopes that he spent time for a reason. And he allows to use this code in any projects without restrictions. And he doesn’t mind emails to the.spite (at) gmail.com (in English, of course)

From the translator: It is worth paying attention to the fact that in the final version the author uses the requestAnimationFrame () function, which is the correct way to draw animations in a loop.

Subscribe, like ...

PS: Translator, I am not much better, but I am learning. All errors and comments send in lichku.

UPD1: It turned out that once this project was already mentioned on Habré. Link

Source: https://habr.com/ru/post/168885/


All Articles