Lecaw

Учимся создавать Web-сайты

Создание 3D меню ресторана

October 1, 2012
CSS
624hits
 
 

Применение CSS 3D transform, может придать дополнительный реализм к обычным веб-элементам. Мы поэкспериментировали с некоторыми простыми веб-сайтами ресторанов и придумали интересное 3D меню ресторана (реальное меню, не "веб" меню). Результат - это шаблон веб-сайта ресторана, в котором присутствует наше 3D меню ресторана. Щелчок по объединенным элементам покажет модальное наложение, содержащее дополнительную информацию.

 Исходные файлыДЕМО

 

Так как эта тестовая структура, она требует приличного пространства и несмотря на то, что мы сделаем наше меню адаптируемым, мы добавим медиа запросы для небольших экранов, где будет использоваться упрощенная структура. Потом мы настроим стили для браузеров, которые не поддерживают 3D CSS transform.

HTML разметка

Наша структура будет состоять из основного контейнера с классом “rm-container” и оболочкой внутри. Оболочка будет содержать три панели. Изначально, нам только нужно отобразить свернутое меню, это - контейнер с классом ”rm-cover”. Последняя панель с классом “rm-right”. Средняя панель - та, которую мы видим в середине, когда открываем 3D меню:

			<div id="rm-container" class="rm-container">			
    <div class="rm-wrapper">
        <div class="rm-cover"></div>
        <div class="rm-middle"></div>
        <div class="rm-right"></div>
    </div><!-- /rm-wrapper -->
</div><!-- /rm-container -->

В контейнерах rm-cover и rm-right у нас будет передняя и задняя сторона:

		<div class="rm-front">
	<div class="rm-content">
		<!-- Some content -->
	</div><!-- /rm-content -->
	
</div><!-- /rm-front -->
<div class="rm-back">
	<div class="rm-content">
		<!-- Some content -->
	</div><!-- /rm-content -->
	<div class="rm-overlay"></div>
</div><!-- /rm-back -->

Теперь добавим градиент, который придаст элементам меню больше реализма. Обратите внимание на то, что передняя сторона будет пустой в последнем элементе.

У средней части меню просто будет другая оболочка:

		<div class="rm-inner">
	<div class="rm-content">
		<!-- Some content -->
	</div><!-- /rm-content -->
	<div class="rm-overlay"></div>
</div><!-- /rm-inner -->

Контент будет состоять из некоторых текстовых элементов, такие как списки и заголовки:

		<div class="rm-content">
	<h4>Appetizers</h4>
	<dl>
		<dt>Bella's Artichokes</dt>
		<dd>Roasted artichokes with chipotle aioli and cream cheese</dd>
		<dt><a href="#" class="rm-viewdetails" data-thumb="images/1.jpg">Green Love Crostini</a></dt>
		<dd>Crostini with young pecorino, grilled figs and arugula & mint pesto</dd>
		
		<dt>Focaccia di Carciofi</dt>
		<dd>Artichoke focaccia with fresh thyme</dd>
		<!-- ... -->
	</dl>
	<h4>Salads & More</h4>
	
	<dl>
		<!-- ... -->
	</dl>
</div><!-- /rm-content -->

Посмотрите на якорь с классом “rm-viewdetails” и атрибут данных “data-thumb”. Мы будем использовать их в качестве контента для модального поля, которое появится при щелчке по ссылке:

		<div class="rm-modal">
	<div style="background-image: url('/images/1.jpg')" class="rm-thumb"></div>
	<h5>Green Love Crostini</h5>
	<p>Crostini with young pecorino, grilled figs and arugula & mint pesto</p>
	<a href="#">See the recipe</a>
	<span class="rm-close-modal">x</span>
</div>

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

CSS

Нам нужно сделать все элементы плавающими, поэтому мы зададим основному контейнеру ширину со значением 1% и добавим CSS perspective:

.rm-container { 
    width: 33%; 
    height: 700px; 
    max-width: 370px; 
    margin: 0 auto 40px auto; 
    position: relative; 
    perspective: 1600px; 
    color: #2a323f; 
}

У оболочки и ее блоков, будет абсолютное расположение и 100% ширина и высота:

.rm-wrapper, 
.rm-wrapper > div { 
    width: 100%; 
    height: 100%; 
    left: 0; 
    top: 0; 
    position: absolute; 
    text-align: center; 
    transform-style: preserve-3d; 
}

Так как мы будем работать с 3D CSS perspective, нам нужно, чтобы стиль преобразования этих элементов был “preserve-3d”.

Обложка должна иметь более высокий z-index, чем все другие элементы, мы установим источник преобразования слева. Зададим задержку для закрытия 3D меню ресторана. Переход к открытому состоянию будет иметь другое значение, мы вернемся к этому позже:

.rm-wrapper .rm-cover { 
    z-index: 100; 
    transform-origin: 0% 50%; 
    transition-delay: 0.2s; 
}

У средней части будет самый низкий z-index. Добавим для неё небольшую тень:

.rm-wrapper .rm-middle { 
    z-index: 50; 
    box-shadow: 0 4px 10px rgba(0,0,0,0.7); 
}

У правой части z-index будет выше, чем у средней части, но ниже чем у обложки. Источник преобразования будет на правом краю и у него не будет задержки при закрытии 3D меню:

.rm-wrapper .rm-right { 
    z-index: 60; 
    transform-origin: 100% 50%; 
    transition-delay: 0s; 
}

У внутренних блоков с классами “rm-front”, “rm-back” и “rm-inner”, будет фоновая текстура в виде бумаги, а также добавим для них тень, которая моделирует многострочную декоративную границу:

.rm-wrapper > div > div { 
    background: #fff url(../images/white_paperboard.jpg); 
    width: 100%; 
    height: 100%; 
    position: absolute; 
    padding: 30px; 
    box-shadow:  
        inset 0 0 0 16px #fff,  
        inset 0 0 0 17px #e6b741,  
        inset 0 0 0 18px #fff,  
        inset 0 0 0 19px #e6b741,  
        inset 0 0 0 20px #fff,  
        inset 0 0 0 21px #e6b741; 
}

Теперь, давайте добавим важные 3D свойства. Для обратной стороны нужно добавить backface-visibility: hidden:

.rm-container .rm-front, 
.rm-container .rm-back { 
    backface-visibility: hidden; 
}

Давайте повернем обратную сторону так, чтобы она была позади передних частей:

.rm-container .rm-back { 
    transform: rotateY(-180deg); 
}

Добавим полупрозрачный css градиент:

.rm-overlay { 
    position: absolute; 
    width: 100%; 
    height: 100%; 
    top: 0; 
    left: 0; 
    pointer-events: none; 
    background: linear-gradient(to right, rgba(0,0,0,0) 0%, rgba(0,0,0,0.05) 100%); 
}

Для среднего наложения мы повернем css градиент к другой стороне:

.rm-middle .rm-overlay { 
    background: linear-gradient(to right, rgba(0,0,0,0) 64%, rgba(0,0,0,0.05) 100%); 
}

Давайте добавим дополнительный отступ для блока с контентом:

.rm-content { 
    padding: 20px; 
}

Модальное наложение будет невидимым, мы установим непрозрачность равную 0 и переведем его по оси Z:

.rm-modal { 
    position: absolute; 
    z-index: 10000; 
    width: 120%; 
    margin-left: -10%; 
    top: 50%; 
    padding: 40px; 
    background: #fff url(../images/white_paperboard.jpg); 
    box-shadow:  
        inset 0 0 0 16px #fff,  
        inset 0 0 0 17px #e6b741,  
        inset 0 0 0 18px #fff,  
        inset 0 0 0 19px #e6b741,  
        inset 0 0 0 20px #fff,  
        inset 0 0 0 21px #e6b741, 
        0 4px 20px rgba(0,0,0,0.4); 
    opacity: 0; 
    pointer-events: none; 
    transform: translateZ(1000px); 
}

Идея состоит в том, чтобы показать модальное наложение, когда мы щелкаем по одной из ссылок в 3D меню ресторана. Таким образом мы уменьшим масштаб меню и заставим модальное наложение появиться из основного. Это понятие вдохновлено от Hakim El Hattab’s modal.

Давайте добавим переходы и определим некоторые классы для открытия 3D меню.

Во-первых, зададим переход для оболочки (для того, чтобы масштабировать её при открытии модального наложения) и для её дочерних элементов, т.е. для трех наших частей меню (для того, чтобы анимировать "открытие" / вращение):

.rm-wrapper, .rm-wrapper > div { transition: all 0.6s ease-in-out; }

У модального наложения также будет переход для преобразования и непрозрачности:

.rm-modal { 
    transition:  
        transform 0.6s ease-in-out, 
        opacity 0.6s ease-in-out; 
}

Идея состоит в том, чтобы класс "rm-open" вызывал 3D меню. Этот класс будет добавлен с JavaScript, когда мы щелкнем по ссылке "View the menu".

При добавлении класса мы определим то, что произойдет со всеми элементами при открытии 3D меню ресторана.

Всем дочерним элементам оболочки, добавим тень:

.rm-container.rm-open .rm-wrapper > div { 
    box-shadow: 0 4px 5px -3px rgba(0,0,0,0.6); 
}

Повернем обложку (без задержки) на -180 градусов по оси Y. Так как мы определили источник преобразования, он откроется слева:

.rm-container.rm-open .rm-cover { 
    transform: rotateY(-180deg); 
    transition-delay: 0s; 
}

Правую часть повернем на 180 градусов, но здесь мы добавим задержку в 0.2 с, чтобы обложка открылась немного раньше:

.rm-container.rm-open .rm-right { 
    transform: rotateY(180deg); 
    transition-delay: 0.2s; 
}

Когда мы щелкнем по одному из пунктов 3D меню, которое является привязкой, мы добавим класс "rm-in" к контейнеру и переместим оболочку вниз по оси Z:

.rm-container.rm-in .rm-wrapper { 
    transform: translateZ(-500px); 
}

Обложка и правая часть будут повернуты больше к внутреннему блоку:

.rm-container.rm-in .rm-cover { 
    transform: rotateY(-150deg); 
} 
  
.rm-container.rm-in .rm-right { 
    transform: rotateY(150deg); 
}

Для этого случая, мы должны установить задержку перехода в 0 секунд:

.rm-container.rm-in .rm-cover,  
.rm-container.rm-in .rm-right, 
.rm-container.rm-nodelay .rm-right { 
    transition-delay: 0s; 
}

Класс "rm-nodelay" является дополнительным классом, который мы будем использовать для правой части меню, когда мы закроем модальное наложение. Помните, у него была задержка перехода при открытии 3D меню.

Модальное наложение появится, переводя его в 0px на оси Z и увеличивая непрозрачность до 1:

.rm-container.rm-in .rm-modal { 
    transform: translateZ(0px); 
    opacity: 1; 
    pointer-events: auto; 
}

Давайте определим медиа запросы для небольших экранов мобильных устройств. Первый медиа запрос просто изменит размеры оболочки:

@media screen and (max-width: 1110px) { 
    .rm-container { 
        height: 800px; 
    } 
}

Так как мы сделали плавающую оболочку, для неё нужно установить большую высоту для размещения текста.

960 пикселей достаточно, чтобы показать развернутое меню с возможностью прокрутки. Для этого мы просто зададим 100% ширину и автоматическую высоту:

@media screen and (max-width: 960px) { 
  
    .rm-container { 
        width: 100%; 
        height: auto; 
        max-width: 460px; 
    }

Все внутренние контейнеры будут иметь относительное расположение, зададим им 100% ширину и автоматическую высоту:

.rm-wrapper,  
.rm-wrapper > div, 
.rm-wrapper > div > div { 
    position: relative; 
    width: 100%; 
    height: auto; 
}

Добавим дополнительные отступы для блока с контентом:

.rm-wrapper > div > div{ 
    margin-bottom: 10px; 
    box-shadow:  
        inset 0 0 0 16px #fff,  
        inset 0 0 0 17px #e6b741,  
        inset 0 0 0 18px #fff,  
        inset 0 0 0 19px #e6b741,  
        inset 0 0 0 20px #fff,  
        inset 0 0 0 21px #e6b741, 
        0 3px 5px rgba(0,0,0,0.2); 
}

Удалим вращения:

.rm-container .rm-back, 
.rm-container.rm-open .rm-cover, 
.rm-container.rm-open .rm-right { 
    transform: rotateY(0deg); 
}

Нам больше не нужны оверлейные элементы:

.rm-overlay, .rm-middle .rm-overlay { 
    display: none; 
}

Установим фиксированную позицию для элемента rm-modal, так, чтобы он располагался сверху при прокрутке:

.rm-container .rm-modal { 
    position: fixed; 
    width: 80%; 
    top: 100px; 
    left: 50%; 
    margin: 0 0 0 -40%; 
    transform: translateZ(0px); 
    transition: opacity 0.6s ease-in-out 0s; 
}

Когда мы щелкаем по пункту меню, появляется модальное наложение:

.rm-container.rm-in .rm-cover, 
    .rm-container.rm-in .rm-right, 
    .rm-container.rm-in .rm-wrapper { 
        transform: rotateY(0deg); 
        transition-delay: 0s; 
    } 
}

Для браузеров, которые не поддерживают 3D CSS transforms, мы будем использовать почти такое же моделирование, но с добавлением медиа запросов. Так как мы используем Modernizr, то просто добавим уже готовые классы.

JavaScript

Начнем с кэширования элементов:

 // основной контейнер 
var $container = $( '#rm-container' ),                       
    // обложка, левая и правая панели 
    $cover = $container.find( 'div.rm-cover' ), 
    $middle = $container.find( 'div.rm-middle' ), 
    $right = $container.find( 'div.rm-right' ), 
    // открытие и закрытие элементов 
    $open = $cover.find('a.rm-button-open'), 
    $close = $right.find('span.rm-close'), 
    // ссылки для каждого рецепта (фотография и детали)
    $details = $container.find( 'a.rm-viewdetails' ),

Инициализация событий для открытия/закрытия 3D меню ресторана, а также для того, чтобы показать дополнительные детали каждого элемента:

init = function() { 
  
        initEvents(); 
  
    }, 
initEvents = function() { 
  
    $open.on( 'click', function( event ) { 
  
        openMenu(); 
        return false; 
  
    } ); 
  
    $close.on( 'click', function( event ) { 
  
        closeMenu(); 
        return false; 
  
    } ); 
  
    $details.on( 'click', function( event ) { 
  
        $container.removeClass( 'rm-in' ).children( 'div.rm-modal' ).remove(); 
        viewDetails( $( this ) ); 
        return false; 
  
    } ); 
      
},

Чтобы открыть/закрыть меню, мы будем добавлять/удалять класс, 'rm-open' от $container. Этот класс с определенными переходами.

Обратите внимание на то, что в конце мы также удаляем классы 'rm-nodelay' и 'rm-in'. Это добавленные классы, для отображения деталей пунктов 3D меню ресторана:

openMenu = function() { 
  
    $container.addClass( 'rm-open' ); 
  
}, 
closeMenu = function() { 
  
    $container.removeClass( 'rm-open rm-nodelay rm-in' ); 
  
},

Наконец, если мы щелкнем по пункту меню, чтобы увидеть детали элемента, то будет отображаться модальное наложение с изображением и текстом для этого элемента. Мы переводим основной контейнер по оси Z и немного поворачиваем левые и правые панели:

viewDetails = function( recipe ) {
 
    var title = recipe.text(),
        img = recipe.data( 'thumb' ),
        description = recipe.parent().next().text(),
        url = recipe.attr( 'href' );
 
    var $modal = $( '<div class="rm-modal"><div class="rm-thumb" style="background-image: url('/ + img + ')"></div><h5>' + title + '</h5><p>' + description + '</p><a href="#">See the recipe</a><span class="rm-close-modal">x</span></div>' );
 
    $modal.appendTo( $container );
 
    var h = $modal.outerHeight( true );
    $modal.css( 'margin-top', -h / 2 );
 
    setTimeout( function() {
 
        $container.addClass( 'rm-in rm-nodelay' );
 
        $modal.find( 'span.rm-close-modal' ).on( 'click', function() {
 
            $container.removeClass( 'rm-in' );
 
        } );
    
    }, 0 );
 
};

Исходные файлыДЕМО

 

 

 

Дополнительная информация

Влерий Аликин - веб-разработчик & дизайнер. Соучредитель и член команды Lecaw.

Эл. почта
RATTING:
(3 голосов)

Оставить комментарий