Пример, как нарисовать красивые 3D облака используя CSS 3D Transformations
Случайно набрел сегодня на еще одну интересную статью об интересных эффектах и решил, что мою любимую Хабра-аудиторию этот пример тоже может заинтересовать. Английский у меня такой-же, как у Азарова украинский, по-этому перевод будет авторский.
Для нетерпеливых: То, что получится в итоге
Вступление
В этой статье, автор попытаеся рассказать и показать как создать красивые 3D облака используя CSS3 Transformations. Автор так-же считает, что иметь базовое понятие о том, как работают эти самые CSS 3 Transformations не будет лишним. Ссылка на туториал здесь
Данный туториал разбит на несколько простых шагов. Каждый шаг основан на коде из предыдущего и содержит ссылки на примеры.
- Создаем мир и камеру
- Добавляем объекты в мир
- Добавляем спрайты к облакам
- Магия
- Пролог
Поооеехааали!
1. Создаем мир и добавляем камеру
Для начала нам нужно создать два div-элемента: точку обзора (камера) и мир. Все остальные элементы мы будем создавать динамически используя JavaScript.
Точка обзора покрывает всю сцены и играет роль камеры. Поскольку камеры для CSS 3D Transformations не существует, мы представим себе, что камера — это стекло через которое мы видим созданный нами мир. Облака мы будем создавать внутри нашего мира с блекджеком и….
Мир — это div-блок, который находится внутри блока камеры и в котором будут находится облака.
Это пример HTML разметки, которая должна у нас получится
<div id="viewport" > <div id="world" ></div> </div>
И добавим стили к нашему миру и камере. Очень важно поместить блок, содержащий нашу сцену (мир), по середине внутри камеры, иначе вся сцена будет рисоватся со смещением.
#viewport { bottom: 0; left: 0; overflow: hidden; perspective: 400; position: absolute; right: 0; top: 0; } #world { height: 512px; left: 50%; margin-left: -256px; margin-top: -256px; position: absolute; top: 50%; transform-style: preserve-3d; width: 512px; }
И немножко кода: мы инициализируем наши объекты, прицепимся к mousemove событию и создадим метод updateView()
/* Определим переменные world и viewport как DOM элементы worldXAngle и worldYAngle с плавающей точкой, которые будут отвечать за вращение мира d число, которое отвечает за расстояние между миром и камерой */ var world = document.getElementById( 'world' ), viewport = document.getElementById( 'viewport' ), worldXAngle = 0, worldYAngle = 0, d = 0; /* Следим за передвижениями мыши и трансформируем координаты в углы, на которые мир будет поворачиватся от -180 до 180 градусов, вертикально и горизонтально */ window.addEventListener( 'mousemove', function( e ) { worldYAngle = -( .5 - ( e.clientX / window.innerWidth ) ) * 180; worldXAngle = ( .5 - ( e.clientY / window.innerHeight ) ) * 180; updateView(); } ); /* Следим, если пользователь крутит мышь за колесо и изменяем расстояние d */ window.addEventListener( 'mousewheel', onContainerMouseWheel ); window.addEventListener( 'DOMMouseScroll', onContainerMouseWheel ); function onContainerMouseWheel( event ) { event = event ? event : window.event; d = d - ( event.detail ? event.detail * -5 : event.wheelDelta / 8 ); updateView(); } /* Изменяем CSS3 свойство transform для мира Changes the transform property of world to be двигаем Z ось на расстояние d поворачиваем ось X на worldXAngle градусов и поворачиваем ось Y на worldYAngle градусов */ function updateView() { world.style.transform = 'translateZ( ' + d + 'px ) \ rotateX( ' + worldXAngle + 'deg) \ rotateY( ' + worldYAngle + 'deg)'; }
Мир у нас будет красным. А у камеры будет градиент, симулирующий небесный свод.
Так-же мы будем следить за передвижениями мыши и менять углы обзора и расстоние между камерой и миром в зависимости от положения этой самой мыши.
%username% подвигай мышкой и узри как мир меняет свою ориентацию в пространстве.
2. Добавляем объекты к миру
Теперь нам нужно добавить div-блоки, внутри которых будут рисоватся наши облака. Каждый новый блок — новое облако. Мы будем создавать абсолютно спозиционированые блоки относительно мира, но используя CSS3 Transformations вместо left и top.
Изначально все эти объкты будут находися по центру мира. Размер этих объектов значения не имеет, так-как это только обертки для облаков. И лучше бы их поместить по центру относительно мира (установить margin-left и margin-top на минус половину высоты и ширины).
.cloudBase { height: 20px; left: 256px; margin-left: -10px; margin-top: -10px; position: absolute; top: 256px; width: 20px; }
И добавим функции createCloud(), которая будет создавать обертку для облака и добавлять ее в DOM и функцию createCloud(), которая будет собственно генерировать несколько облаков.
Еще мы добавим переменную p, которая отвечает за перспективу камеры (свойство заставляющее объекты, которые находятся дальше от камеры выглядеть меньшими по размеру)
d = 0, p = 400, // Перспектива worldXAngle = 0, worldYAngle = 0; viewport.style.webkitPerspective = p; viewport.style.MozPerspective = p; viewport.style.oPerspective = p; /* objects служит для хранения всех облаков layers нужен для хранения текстур (будем использовать не следующем шаге) */ var objects = [], layers = []; /* Удаляем все существующие облака и генерируем новые */ function generate() { objects = []; layers = []; if ( world.hasChildNodes() ) { while ( world.childNodes.length >= 1 ) { world.removeChild( world.firstChild ); } } for( var j = 0; j <; 5; j++ ) { objects.push( createCloud() ); } } /* Создаем placeholder для облака который позиционирован случайным образом в мире Область для каждой оси от -256 до 256 пикселей */ function createCloud() { var div = document.createElement( 'div' ); div.className = 'cloudBase'; var x = 256 - ( Math.random() * 512 ); var y = 256 - ( Math.random() * 512 ); var z = 256 - ( Math.random() * 512 ); var t = 'translateX( ' + x + 'px ) translateY( ' + y + 'px ) translateZ( ' + z + 'px )'; div.style.webkitTransform = t; div.style.MozTransform = t; div.style.oTransform = t; world.appendChild( div ); return div; }
.cloudBase — это простые розовые прямоугольники, помечающие контейнеры для облаков.
Пример тынц
3. Добавляем спрайты к облакам.
Чем дальше в лес, тем интересней! Мы добавим несколько абсолютно спозиционированных .cloudLayer блоков, которые являются спрайтами в каждом из облаков.
.cloudLayer { height: 256px; left: 50%; margin-left: -128px; margin-top: -128px; position: absolute; top: 50%; width: 256px; }
Немножко изменим нашу generateCloud() функцию, теперь она так-же генерирует спрайты внутри облаков.
function createCloud() { var div = document.createElement( 'div' ); div.className = 'cloudBase'; var x = 256 - ( Math.random() * 512 ); var y = 256 - ( Math.random() * 512 ); var z = 256 - ( Math.random() * 512 ); var t = 'translateX( ' + x + 'px ) translateY( ' + y + 'px ) translateZ( ' + z + 'px )'; div.style.webkitTransform = t; div.style.MozTransform = t; div.style.oTransform = t; world.appendChild( div ); for( var j = 0; j < 5 + Math.round( Math.random() * 10 ); j++ ) { var cloud = document.createElement( 'div' ); cloud.className = 'cloudLayer'; var x = 256 - ( Math.random() * 512 ); var y = 256 - ( Math.random() * 512 ); var z = 100 - ( Math.random() * 200 ); var a = Math.random() * 360; var s = .25 + Math.random(); x *= .2; y *= .2; cloud.data = { x: x, y: y, z: z, a: a, s: s }; var t = 'translateX( ' + x + 'px ) translateY( ' + y + 'px ) translateZ( ' + z + 'px ) rotateZ( ' + a + 'deg ) scale( ' + s + ' )'; cloud.style.webkitTransform = t; cloud.style.MozTransform = t; cloud.style.oTransform = t; div.appendChild( cloud ); layers.push( cloud ); } return div; }
4. Магия
Наконец-то мы будет вершить магию! У нас есть массив layers[], который содержит ссылку на каждый спрайт в нашем мире. Так-же у нас есть worldXAngle и worldYAngle. Теперь сделаем так, что-бы перпендикуляр опущенный их поверхности камеры, был так-же перпендикуляром к любому из наших спрайтов. Другими словами спрайты всегда будут к нам лицом.
В очередной раз отредактируем функцию update()
function update (){ for( var j = 0; j < layers.length; j++ ) { var layer = layers[ j ]; layer.data.a += layer.data.speed; var t = 'translateX( ' + layer.data.x + 'px ) translateY( ' + layer.data.y + 'px ) translateZ( ' + layer.data.z + 'px ) rotateY( ' + ( - worldYAngle ) + 'deg ) rotateX( ' + ( - worldXAngle ) + 'deg ) scale( ' + layer.data.s + ')'; layer.style.webkitTransform = t; layer.style.MozTransform = t; layer.style.oTransform = t; } updateView() ; }
Пролог
Вот и все, основная работа почти выполнена. Осталось только добавить текстуры к нашим спрайтам и немножечко подчистить CSS (убрать фоны, рамки и т.д.)
Текстуры можно выбирать абсолютно на любой вкус: облака, грозовые облака, тостеры… Если использовать разные текстуры, то можно добится интереснейших эффектов!
Собственно пример к этому шагу и финальная подчищенная версия
Можно добавлять спрайты в любом порядке, а можно создавать облака в форме овечек и пастухов. Простор для фантазий не ограничен.
Автор данного урока надеется, что потратил время не зря. А еще он разрешает использовать этот код в любых проектах без ограничений. И еще он не против писем на the.spite(at)gmail.com (на английском языке конечно-же)
От переводчика: Стоит обратить внимание на то, что в финальной версии автор использует функцию requestAnimationFrame(), которая является правильным способом рисовать анимации в цикле.
Подписывайтесь, ставьте лайк…
P.S.: Переводчик я некудышней, по-этому не судите строго. Все ошибки и замечания присылайте в личку.
ссылка на оригинал статьи http://habrahabr.ru/post/168885/
Добавить комментарий