В этой статье мы продолжим разбираться в принципах создания анимированной Солнечной системы на 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-изображение с текстурой:
Для помещения изображения поверх солнца снова воспользуемся правилом 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»