Создать 3D-объекты с помощью HTML и CSS совершенно не сложно. Другое дело, что возможности разметки не позволяют делать простым подходом сложные геометрические фигуры без использования дополнительных средств, вроде Canvas и SVG. Тем не менее, даже ограниченные возможности HTML-разметки позволяют делать достаточно красивые вещи. И в этой статье, на примере маленькой посадочной страницы, мы рассмотрим, как сделать 3D объект — куб, грани которого будут представлять собой блоки информации.
| Опубликовал: | Paul.Z |
| Автор: | Paul Z |
| Опубликовано: | 28.12.2020 |
| Просмотров: | 12091 |
| Рейтинг: |
Взгляните на страницу во фрейме, которую мы будем пытаться сделать. Обозначим ее, как аналогичную посадочной, потому что она имеет основные элементы таковой: навигацию и блоки информации. Сверху находятся ссылки, по клику на которые будет осуществляться переход на соответствующие грани куба посредством его вращения. Ниже, в качестве дополнения, добавлены еще пару ссылок, чтобы продемонстрировать приемы 3D в HTML и CSS. Поиграйтесь с HTML-кубом, а затем детально разберем, как это все работает.
Страница c вращающимся кубом, демонстрирующая возможности 3D в HTML и CSS.
И так, вот для начала весь код тела нашей посадочной страницы. Я исключил некоторые CSS-эффекты вроде теней, чтобы не осложнять разбор кода, но вы можете их рассмотреть самостоятельно, открыв исходный код данной посадочной страницы.
<nav>
<a href="#back">Задняя грань</a>
<a href="#top">Верхняя грань</a>
<a href="#front">Фронтальная грань</a>
<a href="#bottom">Нижняя грань</a>
<a href="#left">Левая грань</a>
<a href="#right">Правая грань</a>
</nav>
<section class="cube">
<article class="back">
<img src="town.png" alt="">
</article>
<article class="top">
<img src="car.png" alt="">
</article>
<article class="front">
<h2>Фронтальная грань</h2>
<p>Представьте, что грань — это единица материала и здесь находится какой-то анонс или картинка из галереи.</p>
</article>
<article class="bottom">
<img src="soyuz.png" alt="">
</article>
<article class="left">
<img src="ship.png" alt="">
</article>
<article class="right">
<img src="plane.png" alt="">
</article>
</section>
nav {
display: flex;
justify-content: space-around;
padding: 10px 0 10px;
border-bottom: solid 1px #90f9;
}
nav a {
color: #fff;
border-radius: 10px;
padding: 0 10px;
margin: 10px 10px;
}
.cube {
width: 300px;
height: 300px;
position: absolute;
top: calc(50% - 300px / 2);
left: calc(50% - 300px / 2);
transform-style: preserve-3d;
transform: perspective(700px) rotateX(-36deg) rotateY(-27deg);
transition: all 1s ease;
}
.cube > article {
height: 100%;
width: 100%;
padding: 20px;
box-sizing: border-box;
position: absolute;
transform-style: preserve-3d;
background: linear-gradient(0deg, #070b0f, #111b29);
color: #fff;
line-height: 150%;
letter-spacing: 0.05em;
box-shadow:
0 0 1px #f00,
0 0 1px #f00,
0 0 1px #f00,
0 0 1px #f00,
0 0 10px #d0e9 inset,
0 0 35px #53d5;
}
.back {transform: rotateX(-180deg) translateZ(150px);}
.top {transform: rotateX( 90deg) translateZ(150px);}
.front {transform: translateZ(150px);}
.bottom {transform: rotateX(-90deg) translateZ(150px);}
.left {transform: rotateY(-90deg) translateZ(150px);}
.right {transform: rotateY( 90deg) translateZ(150px);}
.cube > article h2 {
text-transform: uppercase;
color: #900;
font-size: 20px;
font-weight: 600;
}
.cube > article p {
font-weight: 200;
font-size: 15px;
}
.cube > article img {
width: 100%;
}
Навигация по такой посадочной странице была бы затруднительна без JavaScript, поэтому дополним ее несколькими строками простенького кода, который мы также подробно разберем ниже.
function selectEdge(){
const cube = document.querySelector('.cube');
const degs = {
back: {X: -180, Y: 0},
top: {X: 90, Y: 0},
front: {X: 0, Y: 0},
bottom: {X: -90, Y: 0},
left: {X: 0, Y: -90},
right: {X: 0, Y: 90},
}
document.querySelectorAll('nav a').forEach(elem => {
elem.addEventListener('click', (e) => {
e.preventDefault();
let edgeName = e.target.hash.replace(/#/, '');
cube.style.transform = 'perspective(700px) rotateX('+degs[edgeName].X+'deg) rotateY('+degs[edgeName].Y+'deg)';
})
});
}
document.addEventListener('DOMContentLoaded', () => {
selectEdge();
});
Что ж, теперь давайте шаг за шагом разберем, как устроена данная 3D-страница.
Сделать куб на HTML и CSS благодаря возможностям CSS-функциям 3D трансформации очень просто. Всё, что представляется из себя данная геометрическая фигура — это развернутые на требуемые углы и смещенные в определенные стороны полигоны, то есть блочные элементы (div, article или подобное).
Изначально у нас есть набор из шести блоков <article> обернутые в основной блок <section> с классом .cube. Зададим основному блоку размер 300px, указав свойства left и height в его классе. Позиционируем блок абсолютно position: absolute;, чтобы можно было разместить его в любой точке экрана. Эту точку мы обозначим свойствами top и left, рассчитав с помощью CSS-функции calc() центр видимой области страницы. Затем, нам нужно указать основному блоку будущего куба с помощью свойства transform-style: preserve-3d;, что он должен отображать дочерние элементы (будущие грани) в трехмерном представлении. Чтобы увидеть куб во всей красе, к нему нужно сразу применить некоторую подобную трансформацию transform: perspective(700px) rotateX(-36deg) rotateY(-27deg);.
Если этого не сделать, то мы просто увидим одну сторону куба, в то время как другие просто спрячутся за ней. С помощью функции perspective() указывается что-то вроде фокусного расстояния в 3D пространстве, без которого куб будет выглядеть неестественным. А функциями rotateX() и rotateY() мы просто повернем наш HTML куб на презентабельные углы.
Теперь, займемся сторонами куба. Назначим каждому элементу <article> класс соответственно тому, какую сторону он займет. Укажем элементам размеры равные 100%, то есть идентичные родительскому блоку. Позиционируем блоки абсолютно, чтобы они расположились относительно одной и той же точки внутри родительского блока <section>. Зададим фон блокам. Я указал градиент background: linear-gradient(0deg, #070b0f, #111b29); и добавил границ с помощью теней box-shadow, чтобы выделить ребра куба.
И вот мы добрались до самого интересного. Если мы одному дочернему блоку с помощью CSS-трансформации зададим поворот по оси X равным 90° и сместим этот блок на половину его размера по оси оказавшейся перпендикулярной этому блоку, то таким образом мы получим верхнюю грань куба. Аналогично этому, если мы уже развернем другой блок на -90° и так же сместим, то получим нижнюю грань. Фронтальная грань и так развернута как нужно, поэтому для нее потребуется только смещение. А вот для боковых сторон куба поворот нужно осуществлять уже по оси Y.
Таким образом должны получится подобные свойства для граней HTML куба:
.back {transform: rotateX(-180deg) translateZ(150px);}
.top {transform: rotateX( 90deg) translateZ(150px);}
.front {transform: translateZ(150px);}
.bottom {transform: rotateX(-90deg) translateZ(150px);}
.left {transform: rotateY(-90deg) translateZ(150px);}
.right {transform: rotateY( 90deg) translateZ(150px);}
Обратите внимание, что порядок перечисления CSS-функций трансформации важен. Каждая следующая трансформация выполняется относительно результата предыдущей функции. Поэтому не стоит удивляться, что смещение для всех граней куба в 3D-пространстве выполнено только по оси Z.
И вот, куб готов! И теперь таких можно сделать сколько угодно много на странице, просто копируя HTML и меняя лишь значения позиционирования копиям. И тут всё слишком просто, чтобы остановиться на этом. Поэтому пойдем дальше и займемся CSS-анимацией получившегося 3D HTML-объекта.
Анимировать куб еще проще. Для этого потребуется создать один ключевой кадр с промежуточным значением. Проще говоря, наш 3D-объект из своего исходного значения будет плавно переходить к промежуточному кадру и вновь возвращаться в свое исходное положение:
@keyframes rotation {
50% {transform:perspective(700px) rotateX(360deg) rotateY(720deg);}
}
Затем, в класс .cube нужно добавить свойство анимации с указанием объявленных ключевых кадров:
animation: rotation 30s ease-in-out infinite;
Здесь длительность анимации 30 секунд, а функция времени установлена соответственно плавному началу и концу анимации. Используется бесконечное повторение.
Если вы хотите, чтобы анимация куба длилась без замедления и непрерывно, то нужно вместо промежуточного установить конечный 100% {...} ключевой кадр отличный от начального на n * угол полного оборота. То есть, например, если начальный угол по оси Y установлен равным -27° , то конечный ключевой кадр должен содержать угол равный -27° + 360° * n, где n, как вы уже догадались — количество оборотов. Кроме этого, понадобиться сменить временную функцию с ease-in-out на linear.
Веб-страница должна быть интерактивной, а точнее откликаться на действия пользователя. Но в нашем случае нет стандартных событий браузера, чтобы мы могли как-то перемещаться между блоками, то есть гранями куба. Поэтому придется добавить такой интерактивности самостоятельно, прибегнув к помощи JavaScript.
Создадим функцию selectEdge(), которая будет вращать куб на выбранные по ссылкам грани.
Внутри этой функции добавим две константы: элемент нашего 3D HTML-объекта и углы поворота куба соответствующие названиям якорей в ссылках и названиям сторон. Эти данные, для удобства дальнейшего использования, заключены в объект.
const cube = document.querySelector('.cube');
const degs = {
back: {X: -180, Y: 0},
top: {X: 90, Y: 0},
front: {X: 0, Y: 0},
bottom: {X: -90, Y: 0},
left: {X: 0, Y: -90},
right: {X: 0, Y: 90},
}
Следующим шагом требуется описать действия ко клику на каждую из ссылок в меню. Для этого воспользуемся проходом по элементам ссылок меню с помощью метода forEach():
document.querySelectorAll('nav a').forEach(elem => {
elem.addEventListener('click', (e) => {
e.preventDefault();
let edgeName = e.target.hash.replace(/#/, '');
cube.style.transform = 'perspective(700px) rotateX('+degs[edgeName].X+'deg) rotateY('+degs[edgeName].Y+'deg)';
})
});
Поочередно в каждой итерации к элементу ссылки (elem) добавляется событие click, внутри функции обратного вызова описывается следующий сценарий:
Отменяется стандартное событие браузера по клику на ссылку (e) — e.preventDefault();
Получаем название грани куба из якоря ссылки let edgeName = e.target.hash.replace(/#/, '');
Назначаем новые свойства трансформации нашему кубу cube.style.transform ='perspective(…', где в свойства подставляются значения из объекта константы degs.
Завершив написание функции добавим ее вызов, который выполниться после загрузки страницы:
document.addEventListener('DOMContentLoaded', () => {
selectEdge();
}
С JavaScript мы закончили, но остался еще один нюанс. Нам нужно добавить в класс .cube CSS-свойство перехода transition: all 1s ease;. Без него куб будет мгновенно перепрыгивать на новые свойства. Но не забудьте также убрать анимацию из этого класса, которую мы добавляли в предыдущей главе, иначе она будет мешать переходам по ссылкам.
Теперь вы можете совершенствовать и дополнять получившийся результат, чтобы получить действительно объемные и красивые веб-страницы.
В заключении хочу отметить, что разобранный код немного упрощен и отличается от кода посадочной страницы, которая представлена в начале статьи. Оттуда вырезаны пара функций JavaScript для демонстрации и некоторые стилистические эффекты CSS. Это сделано для того, чтобы не усложнять разбор кода для новичков излишними дополнениями. Но вы всегда можете открыть код в браузере и посмотреть как это устроено. А на этом всё. Желаю всем успехов!