Pull to refresh

3D-трансформации средствами CSS

Reading time5 min
Views110K

Увидев на просторах сети пару впечатляющих примеров 3D-трансформаций средствами CSS — заинтересовался, решил разобраться в теме, прочитал несколько статей, вроде бы что-то понял. Но, как известно, теория без практики – как зомби — мертва, хоть и может съесть мозг.

Для усвоения материала необходимо самому сделать что-нибудь любопытное с использованием прочитанного. Какой трехмерный объект сделать легче всего? Пожалуй, кубик. А чтобы результат получился интереснее и красивее, пусть это будет игральный кубик с точками на гранях. Поехали.

Для нетерпеливых и тех, кто смотрит Хабр ради забавных картинок – конечный результат. Работает в Chrome, последних версиях Firefox, Safari. Opera 12.01 — пока никак, ну а про IE вы и сами все знаете.


Стройматериалы


Для начала сделаем заготовку. Блок-контейнер, чтобы отцентрировать все на странице, блок-куб, в который будем вкладывать все составляющие нашей скульптуры, и «развертка» из пяти (шестую добавим потом) будущих граней куба.

«Грани» представляют собой банальные квадратные div’ы, абсолютно спозиционированные в блоке-кубе: «единичку» оставим в центре, она послужит лицевой стороной, а все остальные состыкуем с ней, как на рисунке 1. Покрасим их для веселья и наглядности в разные цвета, добавим внутреннюю тень – и хватит.

Точки на гранях – тоже блоки с position: absolute, border-radius: 50% и внутренней тенью (box-shadow: inset …) для придания иллюзии объема.

Напоследок — два не таких банальных момента.

Для самого куба нужно указать

transform-style: preserve-3d; 

иначе будет использоваться значение по умолчанию – flat, трехмерный мир снова станет плоским и от нашего куба останется только одна грань, которая расположена «лицом» к пользователю.

Маленькая ремарка: здесь и далее css-свойства для трансформаций указаны без префиксов, но в реальном коде, пока что, необходимо их использовать.

Также добавим кубику свойство

transform: perspective(900px);

которое определит, как сильно он будет искажаться из-за перспективы. Чем больше значение, тем дальше находится точка «схождения линий», тем меньше выражен эффект искажения. Значения ниже 200 приводят к диким результатам (например, рисунок 2), около 300 – к заметному искажению (рисунок 3), мы же ограничимся скромными 900, дающими очень умеренный эффект.



С рутиной закончили, начинается интересное.

Сгибаем и клеим


Чтобы превратить нашу развертку в подобие кубика, нужно «согнуть» ее по линиям примыкания крайних граней к грани-1. Для этого нам надо сделать две вещи.

Во-первых, установить для каждой грани ось, вокруг которой она будет вращаться. Дело в том, что по умолчанию блоки поворачиваются вокруг своего центра, а нам нужно несколько иное поведение – на рисунке 4 видно, что грань-5 должна вращаться вокруг отрезка AB, грань-2 вокруг BC, и так далее.

Воспользуемся свойством

transform-origin: y x;

которое сдвигает оси вращения для объекта. Например, для грани-5 нужно ось X (горизонтальная) сдвинуть к нижнему краю, соответственно второе значение transform-origin должно быть 100% (transform-origin: 0 100%;). Значение Y в данном случае неважно, т.к. вращать эту грань мы будем исключительно вокруг горизонтальной оси. Для грани-2 все наоборот – значение X неважно, а Y должно быть установлено в 0, т.е. подойдет значение transform-origin: 0 0.

Во-вторых, и, для данной статьи, «в-главных», для непосредственного вращения элементов используем

transform: rotate3d(x, y, z, deg);

Первые три параметра определяют, вокруг какой из осей координат будет вращаться объект, а последний – на сколько градусов. X, Y и Z задаются не как абсолютные величины, а как соотношение углов. Например, код transform: rotate3d(2, 1, 0, 90deg); заставит объект повернуться на 90 градусов вокруг оси X и на 45 (90 * 1 / 2) градусов вокруг Y. То же самое сделает и строчка transform: rotate3d(90, 45, 0, 90deg).

На рисунке 5 я постарался проиллюстрировать вращение блоков вокруг осей: серый прямоугольник – исходное положение блока, красным выделяется ось, вокруг которой происходит вращение, красный и зеленый прямоугольники – положение блока при повороте на -60 и 60 градусов соответственно.



Повернем грани со второй по пятую на 90 градусов вокруг соответствующих осей, после чего, для наглядности, повернем и сам куб, добавив к .cube свойство transform: rotate3d(1,2,0, -150deg), в результате чего получится нечто, изображенное на рисунке 6.

Куб почти готов, осталось только «закрыть» его.

Закрываем крышкой и подаем к столу


Последнюю грань нужно расположить там же, где «единичку», но «глубже» в экран на 200px (размер граней куба). Сделаем это с помощью свойства

transform: translate3d(x, y, z);

которое позволяет сдвигать блок по любой из трех осей. В нашем случае – движение по оси Z на минус 200 пикселей (отрицательные значения «удаляют» блок, положительные – «приближают»). Одновременно повернем грань на 180 градусов по оси X – хотя, при значениях по умолчанию, «точки» будут отображаться с обеих ее сторон, правильнее будет поставить грань лицевой стороной к зрителю, а не внутрь куба. В итоге свойство transform для нашей «крышки» будет выглядеть следующим образом: transform: translate3d(0,0,-200px) rotate3d(1,0,0,180deg).

Напоследок, с помощью банального opacity: 0.9, добавим немного прозрачности (в Firefox, по невыясненной мной причине, работает только добавление этого свойства для отдельных граней, но не для всего блока-куба сразу) – так наш кубик становится немного похож на стеклянный, выглядит более презентабельно и, если честно, вызывает у автора приступ теплой ностальгии по тем временам, когда «компьютер» был непонятной машиной у папы на работе, а генератором псевдослучайных чисел во множестве игр выступал подобный игральный кубик.

Let’s rock, let’s roll


Кубик готов, но чтобы посмотреть на него с другой стороны, нужно лезть и править CSS – не самый user-friendly вариант, прямо скажем. Поступим по-другому.

В начало блока-контейнера (до куба) добавим четыре кнопки:
<button class="arrow a-top"></button>
<button class="arrow a-bottom"></button>
<button class="arrow a-left"></button>
<button class="arrow a-right"></button>

Опять же с помощью абсолютного позиционирования расположим их со всех четырех сторон контейнера, после чего заставим куб вращаться с помощью примерно такой вот магии:
.a-top:hover ~ .cube    {
     transform: perspective(900px) rotate3d(180,-45,0,-135deg);           
}      

Селектор «~», в отличие от «+», распространяется не только на непосредственного соседа, но и на «отстоящих» дальше, лишь бы они находились на одном уровне в DOM-дереве, чем мы и пользуемся. При наведении мыши на кнопку .a-top повернем следующий за ней блок .cube так, как нам хочется.

Одновременно с этим изменим перспективное искажение с помощью perspective(900px). Как вы помните (помните, правда?), мы установили такое же значение для куба в начале работы, но если не объявить это свойство снова после того, как куб станет к нам задом, а к лесу (на обоях рабочего стола) передом, то и это искажение вывернется вместе с кубом – ближняя к зрителю часть будет уменьшена, а дальняя – расширена. Выглядит неправдоподобно, поэтому будем назначать perspective заново при каждом повороте.

Ну и чтобы наш куб не прыгал из одного положения в другое, а плавно вращался – добавим ему строчку

transition: all 1s ease;

В переводе на русский — скажем кубу плавно изменять все свойства (all), которые будут изменяться, в течение одной секунды (1s) и делать это по функции ease, т.е. в начале анимации плавно ее ускорить, а в конце – плавно остановить. Любители равномерности вместо ease могут указать linear – в таком случае кубик будет двигаться нудно и бездушно.

Однако, Хьюстон, у нас еще одна проблема. Вращение идет вокруг центра грани-1, а не центра самого куба – что, в общем, логично, ведь это она у нас вписана в блок-куб, а все остальные – «загнуты» или вынесены «вглубь». Впрочем, решается это довольно просто: нужно просто «подвинуть» все грани ближе к наблюдателю с помощью все того же translate3d(0,0,100px) так, чтобы центр вращения совпал с центром куба.

Вот теперь получилось неплохо. Можно писать статью на Хабр, чтобы закрепить материал, поделиться изученным с коллегами и просто выпендриться.

Ссылка на демо
Архив для самых любознательных

P.S. Пока писал статью и пробивался в песочницу, на хабре уже появилась похожая статья, но, думаю, они будут хорошо дополнять друг друга.

Обновление:
Важные дополнения от zapimir
— 3DTransform — это пока лишь черновик, а не стандарт, возможны изменения
— IE10 поддерживает эту технологию (демку поправил, добавил префиксы -ms-)
— За шаганием прогресса по планете удобно следить на caniuse.com — в частности здесь
Tags:
Hubs:
+15
Comments27

Articles

Change theme settings