Создать 3D-объекты с помощью HTML и CSS совершенно не сложно. Другое дело, что возможности разметки не позволяют делать простым подходом сложные геометрические фигуры без использования дополнительных средств, вроде Canvas и SVG. Тем не менее, даже ограниченные возможности HTML-разметки позволяют делать достаточно красивые вещи. И в этой статье, на примере маленькой посадочной страницы, мы рассмотрим, как сделать 3D объект — куб, грани которого будут представлять собой блоки информации.
Опубликовал: | Paul.Z |
Автор: | Paul Z |
Опубликовано: | 28.12.2020 |
Просмотров: | 9681 |
Рейтинг: |
Взгляните на страницу во фрейме, которую мы будем пытаться сделать. Обозначим ее, как аналогичную посадочной, потому что она имеет основные элементы таковой: навигацию и блоки информации. Сверху находятся ссылки, по клику на которые будет осуществляться переход на соответствующие грани куба посредством его вращения. Ниже, в качестве дополнения, добавлены еще пару ссылок, чтобы продемонстрировать приемы 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. Это сделано для того, чтобы не усложнять разбор кода для новичков излишними дополнениями. Но вы всегда можете открыть код в браузере и посмотреть как это устроено. А на этом всё. Желаю всем успехов!