Создаём Солнечную систему на чистом CSS3. Часть вторая. Кейфреймы и тени

В этой статье мы продолжим разбираться в принципах создания анимированной Солнечной системы на CSS3. Мы рассмотрим использование keyframe-анимации для движения планет по орбитам вокруг Солнца и реализуем динамические тени на планетах. 

Вот на чем мы остановились в первой части: мы создали HTML-файл с единственным элементом и написали CSS-код для добавления звёздного фона и симуляции солнечного света:

See the Pen CSS 3 Solar System, Step 2 by Independent Software (@independent-software) on CodePen.

<html>
  <head>
    <link rel="stylesheet" href="solar.css"/>
  </head>
  <body>
    <div id="universe">
    </div>
  </body>
</html>

Добавляем Солнце

Прежде чем мы добавим в нашу вселенную какие-либо элементы, мы должны ввести подходящую систему координат. <div> нашей вселенной был прикреплён к элементу <body> через абсолютное позиционирование, но мы хотим, чтобы внутри вселенной позиционирование было относительным. Для этого добавим новый <div> с относительным позиционированием:

<div id="universe">
  <div id="galaxy">
  </div>
</div>

Вот необходимый CSS-код:

#galaxy {
  position: relative;
  width: 100%;
  height: 100%;
}

Все позиции в элементе galaxy теперь будут рассчитываться относительно него. Хотя все текущие элементы и одного размера, но если бы мы добавили хэдеры или футеры вне элемента galaxy, вёрстка бы сбилась.

Давайте добавим элемент <div> для Солнца в центр галактики. Солнце должно быть круглым, поэтому мы превратим прямоугольный <div> в круглый. Это можно сделать, установив border-radius в 50%, что превратит любой квадрат в круг. Зададим размер 30×30 пикселей, и используем отступы для размещения строго по центру экрана. Мы также раскрасим Солнце и добавим приятную жёлтую тень блока для сияния.

#sun {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 30px;
  height: 30px;
  margin-top: -15px;
  margin-left: -15px;
  border-radius: 50%;
  background-color: #FB7209;
  box-shadow: 0 0 60px rgba(255, 160, 60, 0.4);
  z-index: 1;
}

Какое-то маленькое солнце, давайте увеличим длину и ширину. Но работать в пикселях — плохая идея, ведь размеры экранов отличаются. Вместо этого будем задавать размеры в em’ах: они соотносятся со стандартным размером текста в браузере, и с масштабированием проблем не возникнет. Что ещё более важно, все абсолютные величины (размеры, отступы, границы и т.п.) можно задавать в em’ах, и поэтому все их можно масштабировать одновременно, используя font-size. Если у нас есть это:

#sun {
  width: 1em;
  height: 1em;
  margin-top: -.5em;
  margin-left: -.5em;
  ...
}

… и мы добавим это:

#sun {
  font-size: 24em;
}

… то все размеры #sun, выраженные в em’ах, умножатся на 24. Кроме того, увеличатся и отступы, то есть Солнце останется в центре экрана!

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

sun

Для помещения изображения поверх солнца снова воспользуемся правилом background-size:

#sun {
  ...
  background-repeat: no-repeat;
  background-size: cover;
  background-image: url(data:image/png;base64,iVBORw0KG...AAASUVORK5CYII=);
}

Вот результат:

See the Pen CSS 3 Solar System, Step 3 by Independent Software (@independent-software) on CodePen.

Добавляем планету на орбите, используя кейфреймы

Сейчас мы рассмотрим ещё одну фичу CSS3 — keyframe-анимацию, или кейфреймы. Они используются для плавного изменения CSS-свойств с течением времени. Например, если вы хотите, чтобы элемент класса myelement постепенно исчезал, вам стоит написать такой код:

.myelement {
  animation: 'fader' 2s linear;
}

@keyframes fader {
  0%   { opacity: 1; }
  50%  { opacity: 0; }
  100% { opacity: 1; }
}

Сперва утверждение @keyframes используется для создания анимации с уникальным именем fader. Внутри блока keyframes мы создаём три кейфрейма: полная непрозрачность (на 0%, в начале анимации), полная прозрачность (на 50%, в середине анимации) и полная непрозрачность в конце. Непрозрачность — лишь одно из свойств, которые можно анимировать; в дальнейшем мы будем анимировать местоположение и вращение.

Для использования анимации достаточно добавить к определению класса правило animation. Вы задаёте имя анимации, её длительность и временную функцию. мы использовали linear, но можно использовать ease, ease-in или ease-out для ускорения анимации на различных этапах. Подробнее об этом можно почитать здесь.

Также ничего не мешает разделить все свойства анимации. Поскольку планет будет много, имеет смысл написать следующее:

.myelement {
  animation-name: orbit;
  animiation-duration: 2s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

Мы используем animation-iteration-count для создания бесконечного анимационного цикла. Ключевое слово infinite можно добавить и к сокращённому виду записи.

Помните, что в общем случае нужно добавлять и варианты с префиксами: -webkit-animation, -moz-animation и -o-animation — но поскольку мы используем PrefixTree, это нам не нужно.

Поскольку все планеты нашей системы вращаются вокруг Солнца одинаково (меняется лишь диаметр орбиты), мы можем создать общий класс для планеты. Какие-то свойства совпадают и с Солнцем, поэтому упростим:

#sun, .planet {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1em;
  height: 1em;
  margin-top: -0.5em;
  margin-left: -0.5em;
  border-radius: 50%;
}

#sun {
  background-color: #FB7209;
  background-repeat: no-repeat;
  background-size: cover;
  box-shadow: 0 0 60px rgba(255, 160, 60, 0.4);
}

.planet {
  background-color: #202020;
  background-repeat: no-repeat;
  background-size: cover;
}

Добавим планету в наш HTML:

<div id="sun">
</div>
<div id="mercury" class="planet">
</div>

Эта работает, но планета сейчас расположена поверх Солнца, в центре. Прежде чем запустить её движение, нам нужно задать орбиту, поэтому изменим HTML так:

<div id="sun">
</div>
<div id="mercury" class="orbit">
  <div class="planet"></div>
</div>

Теперь у нас есть связка орбита-планета, одинаковая для всех планет. Дадим нашей планете идентификатор #mercury, чтобы мы могли добавить её уникальные свойства. Сперва нарисуем орбиту: дадим ей тонкую, полупрозрачную границу и сделаем её круглой. Для орбиты Меркурия зададим размер 12em.

.orbit {
  position: absolute;
  top: 50%;
  left: 50%;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 50%;
}
#mercury.orbit {
  width: 12em;
  height: 12em;
  margin-top: -6em;
  margin-left: -6em;
}

Орбита нарисована, но Меркурий всё ещё сидит поверх Солнца. Исправим это, задав планете отступ относительно орбиты:

#mercury .planet {
  left: 0%;
  top: 50%;
}

Это поместит Меркурий слева от Солнца, на линию орбиты.

See the Pen CSS 3 Solar System, Step 4 by Independent Software (@independent-software) on CodePen.

Настало время запустить вращение планеты! Провернём такой трюк: будем вращать не планету, а орбиту. Поскольку планета находится в системе координат орбиты, она будет вращаться вместе с орбитой. Создадим кейфреймы для орбит с использованием преобразований. Это новая функция CSS3: вы можете не только анимировать значения свойств, но и преобразовывать элементы, отражая, масштабируя или вращая их. В нашем случае мы зададим вращение относительно оси Z от 0 до 360 градусов.

@keyframes orbit {
  0%   { transform: rotateZ(0deg); }
  100% { transform: rotateZ(-360deg); }
}

Задав анимацию, мы можем применить её к классу орбит:

.orbit {
  ...
  animation-name: orbit;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

Определим длительность анимации для Меркурия:

#mercury.orbit {
  animation-duration: 3s;
}

Вот и всё. Меркурий плавно вращается вокруг Солнца, и мы всё ещё не воспользовались JavaScript. Чистый CSS:

See the Pen CSS 3 Solar System, Step 5 by Independent Software (@independent-software) on CodePen.

Добавляем движущуюся тень на планете, используя анимации и тени блока

Наша Солнечная система будет куда реалистичней, если мы затемним ту часть планеты, которая в текущий момент времени не освещается Солнцем. Это можно реализовать с использованием кейфреймов.

Наша планета вращается вокруг солнца благодаря анимации, применённой к орбите, поэтому мы можем добавим анимацию и к самой планете:

.planet {
  ...
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

#mercury .planet {
  animation-name: shadow-mercury;
  animation-duration: 3s;
}

Мы указали, что анимация планет будет зациклена, а у Меркурия она будет называться shadow-mercury. Определим её:

@keyframes shadow-mercury {
  0%     { box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.5); /* top */ }
  25%    { box-shadow: inset 16px 0 8px rgba(0, 0, 0, 0.5); /* left */ }
  50%    { box-shadow: inset 40px -20px 16px rgba(0, 0, 0, 0.5); /* bottom */ }
  50.01% { box-shadow: inset -40px -20px 16px rgba(0, 0, 0, 0.5); /* bottom */ }
  75%    { box-shadow: inset -16px 0 8px rgba(0, 0, 0, 0.5); /* right */ }
  100%   { box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.5); /* top */ }
}

Внутренние тени используются для отбрасывания теней на планету. Поскольку планеты круглые, тени должны быть такими же. В начале анимации планета находится за Солнцем и полностью освещена. На 50% она полностью в тени. Когда планета проходит перед Солнцем, мы должны переключить анимацию, что мы и делаем на 50.01%. Также заметьте, что на 100% тень должна быть в точности такой же, как и на 0%, для корректного вычисления затенения.

Здесь мы намеренно сделали тень более заметной, чем в оригинальной демке:

See the Pen CSS 3 Solar System, Step 6 by Independent Software (@independent-software) on CodePen.

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

Перевод статьи «Making of the CSS 3 solar system animation»