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


Увидев на просторах сети пару впечатляющих примеров 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 — в частности здесь
Share post

Similar posts

Comments 27

    –5
    IE — не поддерживает.
    Opera — не поддерживает.
    Firefox — Нет сглаживания.
    Chrome — всё гуд.
    Вердикт: я люблю хром! :)
      +5
      IE 10 поддерживает вообще-то, IE не только 6-й версии бывает ;)
        0
        да еще бывает 9 версия, которая (если что) тоже не поддерживает.
          0
          Я к тому, что нужно указывать версии. А не писать по привычке, что IE ничего не понимает, пора уже избавляться от стереотипов. Да и следует указывать, что это пока что не стандарт, а только черновик, более того в который только вчера вносились изменения, так что пока это не более чем игрушка, и к принятию стандарта еще и синтаксис может поменяться.

          Кстати любопытно, естественно все эти трансформации работают только через префиксы. При этом в демке есть свойство с префиксом для Opera (которая даже в 12.5 версии не поддерживается), но при этом нет префикса -ms, хотя у IE10 поддержка этого свойства есть.

          В общем в статью неплохо было бы поставить ссылка на caniuse.com о поддержке CSS3 3D Transforms
            0
            Нет, ты что! никакой предвзятости не было. И стереотипы тут не причем. Просто не стал расписывать во всех версиях.
              0
              Насчет -ms-префиксов и упоминания ИЕ10 — признаю, виновен. Как доберусь до компьютера, обновлю статью и добавлю ссылку на caniuse.

              В свое оправдание могу сказать, что «десятка», все же, еще не вышла :)
                0
                на windows 8 уже есть :)
                кстати да, в тему упомянут браузер который только появился. и то только на недавно релизнутой ОС :)
          +2
          FF15, все работает
            –1
            кроме сглаживания, если понимаете о чем я.
          +4
          Ну кубик, домик уже сделали, осталось дело за троллейбусом из буханки хлеба на CSS3 :)
            0
            Попробую как-нибудь на досуге :)
              +1
              Есть целый микрорайон majorov.su/3d/
                0
                Спасибо, укачало!
                  0
                  Вроде на это похоже
                0
                вкусно, но пока рано :) надо Оперу и FF подождать.
                  0
                  Смотря для чего рано! новые технологии никогда не рано применять. Скажем сделать игрушку для хрома с 3d очень кстати, а кроме игрушку пока сложно придумать применение (если тока плюшки типо красивых графиков, крутящиесе лого на сайте, но к этому же можно и грейсфул деградейшн применить).
                    0
                    Ну, 3D игры на css писать — это больше чем извращение, ведь есть такая штука как webGL. Но для достижения некоторых вспомогательных эффектов, как параллакс бэкграунда, к примеру, использовать эти фишки очень удобно.
                      0
                      просенькую игру вполне себе ок, а вот на webGL сложнее. Вообще игры лучше писать на чем-нибудь другом
                  0
                  Сумма точек на противоположных гранях должна равняться 7. А кубик красивый, да.
                    0
                    Недавно наткнулся на сборник впечатляющих примеров работы с css3 3d transform, если заморочиться, можно сделать простенький движек для браузерной игры, как минимум doom1/2 или стратежку, а то и hal-life3.
                      0
                      Как и вышеупомянутый троллейбус из буханки хлеба :) Для браузерной 3D игры будет куда удобнее использовать webGL
                        0
                        Ну это же не так интересно, как на css3 =)
                      0
                      У меня почему-то совсем не кубик. Хром 21.
                          0
                          Такая же ерунда
                          0
                          Полностью не уверен, но, возможно, дело в маленьком разрешении экрана и каких-то глюках Хрома — у меня на нетбуке с 1024х600 — такая же белиберда, в то время как на десктопе — все хорошо, даже если уменьшить окно до тех же 600 пикселей в высоту…

                          0
                          Интересно, даже в мобильном хроме на андроид работает, только есть небольшой глючек при скроллировании картинки к краю экрана — грань может исчезнуть:



                          P.S. Благодаря этой демке узнал, что в моем Samsung Note в хроме есть hover если держать перо над экраном, а они аннонсируют это как новую фичу Note2!

                          Only users with full accounts can post comments. Log in, please.