Clouds on CSS3 3D Transformations

Original author: Jaume Sánchez
  • Transfer
  • Tutorial

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



I accidentally came across today another interesting article about interesting effects and decided that this example might interest me too, my beloved Habra audience. I have English the same as Ukrainian Azarov, so the translation will be author.

For the impatient: What will be the result

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 concept of how these CSS 3 Transformations work will not be superfluous. Link to the tutorial 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!

  • Create a world and a camera
  • Add objects to the world
  • Add sprites to the clouds
  • Magic
  • Prologue


Poooeeaaali!



1. Create a world and add a camera

First, we need to create two div elements: the viewpoint (camera) and the world. We will create all other elements dynamically using JavaScript.

The viewpoint covers the entire scene and acts as a camera. Since there is no camera for CSS 3D Transformations, we will imagine that the camera is the glass through which we see the world we created. Clouds, we will create in 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



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 whole 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 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 a vault of heaven.
We will also follow the movements of the mouse and change the viewing angles and the distance between the camera and the world, depending on the position of this mouse.
% username% move your 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 block 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, since these are just wrappers for the clouds. And it would be better to place them in the center relative to 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 the p variable, which is responsible for the perspective of the camera (a property that makes objects that are further 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. Tynt

example

3. Add sprites to the clouds.

The farther into the forest, the more interesting! We will add some absolutely positioned .cloudLayer blocks, which 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 [], which contains a link to each sprite in our world. We also have worldXAngle and worldYAngle. Now we will make sure that the perpendicular lowered by their surface of the chamber is also the perpendicular to any of our sprites. In other words, sprites will always face us.

Once again, 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 slightly clean up the CSS (remove backgrounds, frames, etc.)
Textures can be chosen absolutely for every taste: clouds, thunderclouds, toasters ... If you use different textures, you can achieve interesting effects!

Actually an example of this step and the final cleaned version.

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

The author of this lesson hopes that he spent his time not in vain. And he also allows you to use this code in any projects without restrictions. And he is not against letters to the.spite (at) gmail.com (in English, of course)

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

Subscribe, like ...

PS: I’m not a bad translator, but I'm learning. All errors and comments send in PM.

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

Also popular now: