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

  • Tutorial
Передохнём от верстки всяких пользовательских интерфейсов и просто порисуем на 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;}



Ну вот, сцена готова!
Share post

Similar posts

Comments 23

    +1
    Снеговика не хватает.
      +11
      Шедевром, конечно, не назовёшь, но впечатление сугубо позитивное. Спасибо.
        –8
        Неплохо, хотелось бы использование стиля flat. Но в целом суть не в этом, спасибо.
          0
          Так же ж можно создавать резиновые картины!
            +9
            Только мне ещё тут дядю Фёдора с компанией и Печкиным хочется?
              –4
              Не понимаю, ну вот зачем заставлять писаря программировать пичкать CSS лишними функциями.
                +1
                А в домике радуется и весело танцует Бендер? :)
                Ссылка на Бендера
                  +5
                  Круто, восхищен! Мне, с++ программисту уже и не понять, как далеко ушли возможности web от простейшей html разметки, с которой все начиналось когда-то очень давно. Точее, в общих чертах понятно, но это нужно иметь особый стиль мышления, чтобы вручную описывать векторную графику и рисовать неплохие анимированные картины на эффектах типа градиента, скругления прямоугольника и трансформации… :)
                    +1
                    Всё-таки это задачка для SVG.
                      +2
                      Забавно, что без всяких префиксов старая опера справилась лучше чем новый фф.
                      Заголовок спойлера
                      image
                        0
                        Ну-ну:
                        image
                          0
                          Значит мой фф сломался.
                            0
                            Там нужно не только префиксы удалять, но и синтаксис смотреть. Местами используются устаревшие конструкции (вроде отсутствия ключевого слова «to» в описании градиентов).
                              0
                              Я ничего не удалял и не добавлял, просто открыл ссылку.
                                +1
                                А, ну так там почти везде префиксы -webkit- стоят…
                        • UFO just landed and posted this here
                            +1
                            Я говорил про старую Оперу, она на движке Presto.
                            • UFO just landed and posted this here
                          +1
                          alexclimber, а зачем вы вообще используете префиксы? Причем местами используете (к примеру, для градиентов), местами нет, местами устаревший синтаксис, местами совремнный.
                            0
                            Различный синтаксис префиксов банально результат копипаста из разных css генераторов. Да можно сделать круче, критикуйте =) Но статья не про то как идеально верстать. Эта сцена была создана мной чисто «по фану». Ну а поскольку из нее можно выжать некоторую долю полезности, процесс ее создания был оформлен в эту в статью.
                          • UFO just landed and posted this here
                            • UFO just landed and posted this here
                              +1
                              Use Autoprefixer, Luke.

                              Only users with full accounts can post comments. Log in, please.