Плывущие облака

Автор: Евгений Рыжков Дата публикации: 05.10.2012

Задача

Сделать плывущие «живые» облака.

Решение

Применяем методику используемую для построения облаков в играх. Это не супер-пупер реалистично, но зато производительно. Метод основан на многослойности объекта. Каждый слой - это текстура, к которой применяются случайные трансформации, что в сумме дает объект неплохо напоминающий облако.

<div id="sky">
   <div id="world"></div>
</div>
#sky {
	height: 400px;
	position: relative;
	-moz-perspective: 1000px;				// глубина сцены
	-webkit-perspective: 1000px;
	perspective: 1000px;
	overflow: hidden;
	background:-moz-linear-gradient(top, #072ac0, #4466f9);
	background:-webkit-linear-gradient(top, #072ac0, #4466f9);
	backround-image: linear-gradient(top, #072ac0, #4466f9);
}
#world {
	-webkit-transform-style: preserve-3d;
	-moz-transform-style: preserve-3d;	
	transform-style: preserve-3d;				// объекты поддаются 3d траснформациям
	position: relative;
	height: 100%;
}
#world>div {
	-webkit-transform-style: preserve-3d;
	-moz-transform-style: preserve-3d;	
	transform-style: preserve-3d;				
}
.cloud {
	position: absolute;
}
.cloudLayer {
	position: absolute;
	left: 50%;
	top: 50%;
	width: 356px;		/* размеры не обязательно подстраивать под размеры текстуры */
	height: 356px;
	margin-left: -178px;
	margin-top: -178px;
	-webkit-transition: opacity .5s ease-out;
	-moz-transition: opacity .5s ease-out;
	transition: opacity .5s ease-out;
}
	var world = document.getElementById('world'),		// сцена
			nClouds,																		// число облаков
			arrClouds = [],
			arrLayersClouds = []
			nSkyWidth = world.offsetWidth,
			nSkyHeight = world.offsetHeight;
	
	/* чем меньше экран - тем меньше нужно облаков */		
	if(world.offsetWidth > 1500) nClouds = 5;
	else nClouds = 3;

	/* формируем облака */
	for(i=0; i<nClouds; i++)
	{
		arrClouds.push( createCloud(i) );
	}
	
function createCloud(nCloudCount) {
 
    var oCloud = document.createElement( 'div');
		
    oCloud.className = 'cloud';
		
		/* стартовое положение облаков */
    var	nCloudX = nCloudCount*200 - Math.random()*200,				// чтобы была нужная кучность +  равномерно распределить облака по небу
				nCloudY = nSkyHeight - ( Math.random() * nSkyHeight );

		oCloud.style.left = nCloudX + &apospx';
		oCloud.style.top = nCloudY+'px';
	
		var nCloudSpeed = Math.random()*.15;	// скорость движения облаков
		oCloud.data = {
			x: nCloudX,
			speed: nCloudSpeed
		}

    world.appendChild( oCloud );
		
		
    /* формируем слои облака */
		var nLayers =  5 + Math.round( Math.random() * 10), // к-во слоев
				oCloudLayer,
				sCloudTextrure = 'cloud-3.png',									// текстура
				nCloudLayerX,
				nCloudLayerY,
				nCloudLayerZ,
				nCloudLayerA,
				nCloudLayerS,
				nCloudLayerSpeed;
		
    for( var j = 0; j < nLayers; j++ ) {
			oCloudLayer = document.createElement( 'img' );
			
			oCloudLayer.setAttribute( &apossrc', sCloudTextrure );
			oCloudLayer.className = 'cloudLayer';
			
			/* х-ки слоя */
			nCloudLayerX = 256 - ( Math.random() * 512 );
			nCloudLayerY = 256 - ( Math.random() * 512 );
			nCloudLayerZ = 100 - ( Math.random() * 200 );	//
			nCloudLayerA = Math.random()*360;
			nCloudLayerS = 1.1 + Math.random();						// чем больше коэффициент, тем облако больше (ближе)

			if(nCloudLayerS > 0.5) nCloudLayerSpeed = Math.random()*.03;		// скорость вращения, чем слой ближе к нам, тем больше скорость вращения
			else nCloudLayerSpeed = Math.random()*.02;
			
					
			nCloudLayerX *= .2; nCloudLayerY *= .2;
			oCloudLayer.data = { 
				nCloudLayerX: nCloudLayerX,
				nCloudLayerY: nCloudLayerY,
				nCloudLayerZ: nCloudLayerZ,
				nCloudLayerA: nCloudLayerA,
				nCloudLayerS: nCloudLayerS,
				nCloudLayerSpeed: nCloudLayerSpeed
			};
			var t = 'translateX( ' + nCloudLayerX + 'px ) translateY( ' + nCloudLayerY + 'px ) translateZ( ' + nCloudLayerZ + 'px ) rotateZ( ' + nCloudLayerA + 'deg ) scale( ' + nCloudLayerS + ' )';
			oCloudLayer.style.webkitTransform = t;
			oCloudLayer.style.MozTransform = t;
			oCloudLayer.style.oTransform = t;
		
			oCloud.appendChild( oCloudLayer );
			arrLayersClouds.push( oCloudLayer );
		}
			
        return oCloud;
}

function update (){
		
		for( var j = arrLayersClouds.length; j--; ) {
			var layer = arrLayersClouds[ j ];
					
			layer.data.nCloudLayerA += layer.data.nCloudLayerSpeed;
			var t = 'translateX( ' + layer.data.nCloudLayerX + 'px ) translateY( ' + layer.data.nCloudLayerY + 'px ) translateZ( ' + layer.data.nCloudLayerZ + 'px ) rotateZ( ' + layer.data.nCloudLayerA + 'deg ) scale( ' + layer.data.nCloudLayerS + ')';
			layer.style.webkitTransform = t;
			layer.style.MozTransform = t;
		}
		
		for (  var j = arrClouds.length; j--;)
		{
			var object = arrClouds[j];
			
			if(object.data.x>nSkyWidth+300) object.data.x = -300;
			object.data.x+=object.data.speed;
			var sCloudT = 'translateX('+ object.data.x+'px )';
			object.style.webkitTransform = sCloudT;
			object.style.MozTransform = sCloudT;
		}
		
	
		requestAnimationFrame( update );
		
	}
	update();

Живой пример. Работает в:

  • IE 10
  • Firefox
  • Safari 5+
  • Chrome
  • iOs 4+
  • Android 3+

Материалы