Как стать автором
Обновить

Рисуем анимированную сцену с помощью css

Время на прочтение 7 мин
Количество просмотров 50K
Передохнём от верстки всяких пользовательских интерфейсов и просто порисуем на CSS. Рисовать будем такую вот сцену:



Смотреть на jsfiddle.

В статье я попробую описать пошаговое создание этой сцены.

Описанный код работает на webkit браузерах (Chrome, Safari, Opera последних версий). Для работы в других браузерах вместо префиксв -webkit нужно воспользоваться соответствующими префиксами (-moz, -ms, -o).

Создаем сцену и небо
		<div class="scene"></div>
        

		.scene {
			position: relative;
			width: 800px;
			height: 600px;
			margin: 50px auto;
			overflow: hidden;
			background-image: -webkit-linear-gradient(top, #011428, #032a54);
		}
	

с помощью linear-gradient(top, #011428, #032a54) мы заполнили сцену градиентом от темноватого (#011428) до более светлого (#032a54) оттенка синего начиная сверху (top)



Рисуем сугробы. За основу возьмем элипсы которые можно создать из div'ов задав им border-radius: 50%

		.ground {
			position: absolute;
			width: 770px;
			height: 200px;
			border-radius: 50%;
			background-color: #99a;
		}
	




Позиционируем 4 эллипса внизу сцены
	<div class="ground back-1"></div>
	<div class="ground back-2"></div>
	<div class="ground front"></div>
	<div class="ground front-2"></div>
         

.ground {
	width: 400px;
	background-color: #f5f5f5;
	border-radius: 50%;
	width: 770px;
	height: 200px;
	position: absolute;
	bottom: 0;
	margin-bottom: -80px;
	right: 140px;
	background-color: #99a;
}
.ground.back-1 {left: 180px;}
.ground.front {margin-bottom: -124px;left: -27px;}
.ground.front-2 {margin-bottom: -109px;right: -508px;}

	




Делаем сугробы объемными добавив к классу .ground внутренню тень
	box-shadow: 0 0 100px #457eb2 inset;




Рисуем луну и звезды
		<div class="moon"></div>
		<div class="stars">
			<div class="star"></div>
			<div class="star"></div>
			<div class="star"></div>
			<div class="star"></div>
			<div class="star"></div>
			<div class="star"></div>
			<div class="star"></div>
		</div>
       

.moon {
	position: absolute;
	width: 60px;
	height: 60px;
	top: 100px;
	left: 100px;
	background-color: #ffffaa;
	border-radius: 50%;
}

.stars {
	position: absolute;
	width: 100%;
	height: 100%;
}

.star {
	position: absolute;
	border-radius: 50%;
	width: 1px;
	height: 1px;
	background-color: #ffffff;
}

.star:nth-child(1) {top: 100px;left: 685px;}
.star:nth-child(2) {top: 37px;left: 537px;}
.star:nth-child(3) {top: 150px;left: 350px;}
.star:nth-child(4) {top: 50px;left: 320px;}
.star:nth-child(5) {top: 30px;left: 755px;}
.star:nth-child(6) {top: 70px;left: 483px;}
.star:nth-child(7) {top: 18px;left: 80px;}




Добавляем луне эффект свечения
	box-shadow: 0 0 40px #ffffaa;
        

И звездам тоже:
box-shadow: 0 0 10px 2px white;





Теперь приступаем к дому. Дом будет состоять из крыши, стены, окна и дымохода. Описываем все это версткой:
		<div class="house">
			<div class="chimney"></div>
			<div class="roof">
				<div class="roof-wall"></div>
			</div>
			<div class="wall"></div>
			<div class="window"></div>
		</div>

		.house {
			position: absolute;
			width: 300px;
			height: 365px;
			bottom: 50px;
			right: 110px;
		}


Рисуем стену. Эффект бревенчатого сруба можно получить с помощью повторяющегося градиента:
		.house .wall {
			width: 100%;
			height: 200px;
			position: absolute;
			bottom: 0;
			background-color: #180c00;
			background: repeating-linear-gradient(to bottom, #573808 0%,#3a1e12 15%);
		}
	




Делаем окошко. Это будет div с желтым фоном и с коричневой рамкой

		.house .window {
			position: absolute;
			height: 70px;
			width: 65px;
			background-color: #cccc00;
			border: 5px solid #3a1e12;
			bottom: 53px;
			left: 110px;
			box-shadow: 0 0 5px black;
		}
	




Добавляем к нему еще раму и эффект свечения с помощью box-shadow

		<div class="window">
			<div class="frame"></div>
			<div class="frame"></div>
			<div class="light"></div>
		</div>

		.house .window .frame:nth-child(1) {
			position: absolute;
			height: 100%;
			left: 50%;
			margin-left: -3px;
			width: 7px;
			background-color: #3a1e12;
		}

		.house .window .frame:nth-child(2) {
			position: absolute;
			width: 100%;
			top: 30%;
			height: 7px;
			background-color: #3a1e12;
		}

		.house .window .light {
			width: 100%;
			height: 100%;
			background-color: #ffff00;
			opacity: 0.5;
			box-shadow: 0 0 100px yellow;
		}
	




Создаем крышу. Это будет div бордер которого послужит кровлей.
	.house .roof .roof-wall {
		position: absolute;
		width: 280px;
		height: 280px;
		background-color: #573808;
		left: 25px;
		top:60px;
		border: 5px solid #3a1e12;
		box-shadow: 0 0 30px black inset;
	}
	




Для создания эффекта вагонки снова воспользуемся повторяющимся градиентом, который повернем на 45 градусов
		background: repeating-linear-gradient(45deg, #573808 0%,#573808 5%,#3a1e12 5%,#3a1e12 5%,#3a1e12 5%,#573808 5%,#3a1e12 6%)
	




Крыша готова, ставим ее на дом повернув на 45 градусов с помощью transform: rotate(45deg) и отрезав половину с помощью overflow: hidden у контейнера
		.house .roof {
			width: 340px;
			height: 170px;
			right: -20px;
			position: absolute;
			overflow: hidden;
		}

		.house .roof .roof-wall {
			position: absolute;
			width: 280px;
			height: 280px;
			background-color: #573808;
			-webkit-transform: rotate(45deg);
			left: 25px;
			top:60px;
			border: 5px solid #3a1e12;
			box-shadow: 0 0 30px black inset;
			background: repeating-linear-gradient(45deg, #573808 0%,#573808 5%,#3a1e12 5%,#3a1e12 5%,#3a1e12 5%,#573808 5%,#3a1e12 6%);
		}
	




Приделываем трубу, нарисованную с помощью градиента
		.house .chimney {
			position: absolute;
			height: 80px;
			width: 30px;
			top: 58px;
			left: 20px;
			background: linear-gradient(to right, rgba(42,41,45,1) 0%,rgba(80,84,91,1) 36%,rgba(22,27,33,1) 100%);
		}
	


И добавляем шероховатости бревнам, наложив на них пару повторяющихся коричневых градиентов переходящих в прозрачность

		<div class="wall">
			<div class="crack"></div>
			<div class="crack"></div>
		</div>

		.house .wall .crack {
			position: absolute;
			width: 100%;
			height: 100%;
			opacity: 0.5;
			background: repeating-linear-gradient(3deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 2%, rgba(0,0,0,0) 2%,#3a1e12 2%,#3a1e12 2%,#573808 2%,#3a1e12 3%);
		}

		.house .wall .crack:nth-child(2) {
			opacity: 0.3;
			background: repeating-linear-gradient(-4deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 2%, rgba(0,0,0,0) 2%,#3a1e12 2%,#3a1e12 2%,#573808 2%,#3a1e12 3%);
		}
	


Ну вот, у нас получилась уже вполне миленькая картинка



Настало время оживить нашу статичную картинку анимациями. Для начала сделаем чтобы из дымохода появлялись клубы дыма.
Пока что нарисуем одно облако дыма. Это будет полупрозрачный овал со светлым box-shadow ввокруг и радиальным градиентом.
		<div class="smoke-area">
			<div class="smoke"></div>
		</div>

		.smoke-area .smoke {
			position: absolute;
			width: 30px;
			height: 30px;
			border-radius: 50%;
			box-shadow: 0 0 20px lightgray;
			background: radial-gradient(ellipse at center, rgba(206,220,231,1) 33%,rgba(89,106,114,0) 100%);
			top: 120px;
			left: 20px;
		}
	




С помощью ключевых кадров опишем траекторию движения и трансформацию этого облака. Оно будет постепенно увеличиваться и становиться прозрачным

		@-webkit-keyframes smoke-move {
			0% {top: 120px; left: 20px}
			20% {top: 107px; left: 25px}
			30% {top: 95px; left: 35px; opacity: 0.9}
			40% {top: 80px; left: 40px; }
			50% {top: 65px; left: 50px; }
			60% {top: 50px; left: 62px; }
			70% {top: 35px; left: 75px; }
			80% {top: 25px; left: 90px; }
			90% {top: 15px; left: 117px; }
			100% {top: 7px; left: 127px; opacity: 0; width: 90px; height: 60px}
		}
	


Теперь назначим только что описанную анимацию к нашему облаку, добавив к классу .smoke свойство
	-webkit-animation: smoke-move 2.3s linear infinite
	




Отлично у нас уже есть рабочий дымоход, но в полноценном виде он должен непрерывно ипускать много клубов дыма, так что добавим еще нескколько штук в верстку.

	<div class="smoke-area">
		<div class="smoke"></div>
		<div class="smoke"></div>
		<div class="smoke"></div>
		<div class="smoke"></div>
		<div class="smoke"></div>
		<div class="smoke"></div>
		<div class="smoke"></div>
		<div class="smoke"></div>
	</div>

Теперь облаков дыма несколько, но пока что толку от этого мало, так как все они движутся одновременно по одной траектории и все это выглядит как одно облако. Описывать новую анимацию для каждого облака слишком топорно и утомительно. Тут хочется какой-то случайности в их движении, например применить что-нибудь типа Math.random() с помощью javascript, но поскольку цель сделать сцену используя только css, прийдется выкручиваться подругому. В нашем случае можно просто заново использовать для каждого элемента уже описанную анимацию smoke-move, но с разным временем проигрывания:
		.smoke-area .smoke:nth-child(2) {
			-webkit-animation: smoke-move 2.5s linear infinite
		}

		.smoke-area .smoke:nth-child(3) {
			-webkit-animation: smoke-move 2.7s linear infinite
		}

		.smoke-area .smoke:nth-child(4) {
			-webkit-animation: smoke-move 2.2s linear infinite
		}

		.smoke-area .smoke:nth-child(5) {
			-webkit-animation: smoke-move 2.1s linear infinite
		}

		.smoke-area .smoke:nth-child(6) {
			-webkit-animation: smoke-move 2s linear infinite
		}

		.smoke-area .smoke:nth-child(7) {
			-webkit-animation: smoke-move 2.9s linear infinite
		}
	

Теперь клубы дыма движутся по той-же траектории но за различное время что дает видимость их случайного движения:



Через какое-то время цикл анимации движения облаков дыма начнет повторяться, но при достаточно большом количестве циклов этого будет не сильно заментно. Чтобы подобрать наибольший цикл движения в качестве времени анимации нужно указывать взаимно простые числа.


И в качестве изюминки добавим несколько падающих звездочек. Как и раньше начнем с одной, добавим ее как .meteor в div.meteors
		<div class="meteors">
			<div class="meteor"></div>
		</div>

	.meteors {
		position: absolute;
		width: 100%;
		height: 100%;
	}
	


Описание css начнем с хвоста нашего метеора. Это будет белая линия переходящая в прозрачность.
	.meteor {
		position: absolute;
		top: 50px;
		left: 280px;
		width: 300px;
		height: 1px;
		-webkit-transform: rotate(-45deg);
		background-image: -webkit-linear-gradient(left, white, rgba(255,255,255,0));
	}
	



Теперь нарисуем звездочку на конце этого хвоста:
		.meteor:before {
			content: ' ';
			position: absolute;
			width: 4px;
			height: 5px;
			background-color: white;
			border-radius: 50%;
			box-shadow: 0 0 14px 4px white;
			margin-top: -2px;
		}
	


Опишем анимацию для метеора, будем его двигать меняя отступы создавая эффект полета под наклоном 45 градусов. Метеор будет падать и постепенно исчезать.
	@-webkit-keyframes meteor {
		0% {margin-top: -300px; margin-right: -300px; opacity: 1}
		8% {opacity: 0}
		10% {margin-top: 300px; margin-left: -600px; opacity: 0}
		100% {opacity: 0}
	}
	

Запускаем метеор в полет:
		.meteor {top: 100px;left: 480px;-webkit-animation: meteor 10s linear infinite;}
	


Нужно больше метеоров!
		<div class="meteors">
			<div class="meteor"></div>
			<div class="meteor"></div>
			<div class="meteor"></div>
		</div>	

	.meteor:nth-child(1) {top: 100px;left: 480px;-webkit-animation: meteor 10s linear infinite;}
	.meteor:nth-child(2) {top: 200px;left: 280px;-webkit-animation: meteor 10s linear infinite;}
	.meteor:nth-child(3) {top: 250px;left: 790px;-webkit-animation: meteor 9s linear infinite;}



Ну вот, сцена готова!
Теги:
Хабы:
+37
Комментарии 23
Комментарии Комментарии 23

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн