Создаем меню в стиле сайта Google Nexus 7
В данном уроке мы хотим рассказать вам о том, как разработать боковое меню со страницы Google Nexus 7. Оно выезжает в сопровождении очень красивого эффекта. При наведении на специальную иконку меню, также открываются и иконки меню. Кроме этого при нажатии по этой кнопке, вам будет отображено полное боковое меню. Первым пунктом данного меню является поиск, оформленный таким же образом, как и другие пункты меню. Мы воссоздадим это меню при помощи ненумерованных, вложенных списков и нескольких CSS-переходов.
Мы воспользуемся javascript, чтобы применить классы для эффектов открывания и управления событиями при наведении и нажатии. И так, давайте приступим.
Шаг 1. HTML
Наше меню будет состоять их 2 основных элементов: основное меню – то, которое вы можете видеть вверху страницы в виде шапки сайта, и боковое меню. Первому меню мы зададим класс gn-menu-main, и обернем второе меню в элемент ‘nav’. Конечно же, вы можете использовать ту структуру, которую вам хочется.
1 2 3 4 5 6 7 8 9 10 11 12 |
</pre> <ul class="gn-menu-main"> <ul class="gn-menu-main"> <li class="gn-trigger"><a class="gn-icon gn-icon-menu"><span>Menu</span></a> <nav class="gn-menu-wrapper"><!-- ... --></nav></li> <li><a href="#">Codrops</a></li> <li><!-- ... --></li> </ul> </ul> <ul class="gn-menu-main" id="gn-menu"><!-- ... --></ul> <pre> |
Ядром нашего меню будет ненумерованный список с классом gn-menu. Он будет состоять из элементов списка, некоторые из которых будут иметь дополнительный список. Первый пункт меню будет представлен в виде строки поиска:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
</pre> <div class="gn-scroller"> <ul class="gn-menu"> <ul class="gn-menu"> <li class="gn-search-item"><input class="gn-search" type="search" placeholder="Search" /> <a class="gn-icon gn-icon-search"><span>Search</span></a></li> <li><a class="gn-icon gn-icon-download">Downloads</a> <ul class="gn-submenu"> <li><a class="gn-icon gn-icon-illustrator">Vector Illustrations</a></li> <li><a class="gn-icon gn-icon-photoshop">Photoshop files</a></li> </ul> </li> <li><a class="gn-icon gn-icon-cog">Settings</a></li> <li><!-- ... --></li> </ul> </ul> <ul class="gn-menu"><!-- ... --></ul> </div> <!-- /gn-scroller --> <pre> |
С разметкой разобрались, давайте перейдем к следующему шагу.
Шаг 2. CSS
Давайте начнем с указания border-box для всех свойств box-sizing:
1 2 3 4 5 |
*, *:after, *::before { box-sizing: border-box; } |
Так как мы будем использовать иконический шрифт для иконок, нам нужно открыть IcoMoon, и выбрать несколько красивых иконок из набора Eco Ico от Matthew Skiles.
1 2 3 4 |
@font-face { font-weight: normal; font-style: normal; font-family: 'ecoicons'; |
Для начала давайте оформим все списки:
1 2 3 4 5 6 7 8 9 10 11 12 |
.gn-menu-main, .gn-menu-main ul { margin: 0; padding: 0; background: white; color: #5f6f81; list-style: none; text-transform: none; font-weight: 300; font-family: 'Lato', Arial, sans-serif; line-height: 60px; } |
Теперь давайте укажем стили для основного списка. Он будет зафиксирован вверху страницы, и мы зададим ему высоту равную 60 пикселям:
1 2 3 4 5 6 7 8 |
.gn-menu-main { position: fixed; top: 0; left: 0; width: 100%; height: 60px; font-size: 13px; } |
Базовые стили для всех ссылок в нашем меню и подменю будут следующими:
1 2 3 4 5 6 7 |
.gn-menu-main a { display: block; height: 100%; color: #5f6f81; text-decoration: none; cursor: pointer; } |
Здесь у нас не будет анкора, который будет заполнять пункт списка, поэтому давайте определим hover-стиль для li и укажем, что будет происходить с иконкой (анкором) и самим элементом li:
1 2 3 4 5 6 |
.no-touch .gn-menu-main a:hover, .no-touch .gn-menu li.gn-search-item:hover, .no-touch .gn-menu li.gn-search-item:hover a { background: #5f6f81; color: white; } |
Дочерний элемент пункта списка будет выравнен по левой стороне, и будет оформлен правой границей:
1 2 3 4 5 6 7 |
.gn-menu-main &amp;gt; li { display: block; float: left; height: 100%; border-right: 1px solid #c6d0da; text-align: center; } |
Мы установим свойство user-select на none, а свойству ширины (width) зададим то же значение, что указано в высоте всего пункта.
1 2 3 4 5 |
.gn-menu-main li.gn-trigger { position: relative; width: 60px; user-select: none; } |
Последний пункт нашего основного списка будет выравнен по правому краю, и нам также нужно будет слегка изменить границу:
1 2 3 4 5 |
.gn-menu-main &amp;gt; li:last-child { float: right; border-right: none; border-left: 1px solid #c6d0da; } |
Анкоры основного меню будут оформлены небольшими отступами, а также мы применим немного другой стиль текста:
1 2 3 4 5 6 |
.gn-menu-main &amp;gt; li &amp;gt; a { padding: 0 30px; text-transform: uppercase; letter-spacing: 1px; font-weight: bold; } |
Давайте удалим выравнивания при помощи следующего миниатюрного хака clearfix от Nicolas Gallagher:
1 2 3 4 5 |
.gn-menu-main:after { display: table; clear: both; content: ''; } |
Изначально нам нужно скрыть меню, поэтому мы зададим ему значение negative left (равное его ширине):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
.gn-menu-wrapper { position: fixed; top: 60px; bottom: 0; left: 0; overflow: hidden; width: 60px; /* will be transitioned to 340px */ border-top: 1px solid #c6d0da; background: white; transform: translateX(-60px); /* will be transitioned to 0px */ transition: transform 0.3s, width 0.3s; } .gn-scroller { position: absolute; overflow-y: scroll; width: 370px; height: 100%; } .gn-menu { border-bottom: 1px solid #c6d0da; text-align: left; font-size: 18px; } |
Давайте добавим тень блока, чтобы отделить пункты списка. Это поможет нам избежать двойных линий при скрывании пунктов дополнительного меню:
1 2 3 4 |
.gn-menu li:not(:first-child), .gn-menu li li { box-shadow: inset 0 1px #c6d0da } |
Давайте добавим переход к пунктам списка дополнительного меню, и выставим их изначальную высоту на 0:
1 2 3 4 5 |
.gn-submenu li { overflow: hidden; height: 0; transition: height 0.3s; } |
Цвет здесь будет немного светлее родительских пунктов меню:
1 2 3 |
.gn-submenu li a { color: #c1c9d1 } |
Теперь давайте стилизуем отдельный пункт с поиском и поле ввода запроса. Нам нужно, чтобы оно было едва заметным, как это реализовано на странице Google Nexus, поэтому мы зададим ему прозрачный цвет фона и сделаем так, чтобы placeholder-элементы выглядели как обычные пункты меню:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
input.gn-search { position: relative; z-index: 10; padding-left: 60px; outline: none; border: none; background: transparent; color: #5f6f81; font-weight: 300; font-family: 'Lato', Arial, sans-serif; cursor: pointer; } /* placeholder */ .gn-search::-webkit-input-placeholder { color: #5f6f81 } .gn-search:-moz-placeholder { color: #5f6f81 } .gn-search::-moz-placeholder { color: #5f6f81 } .gn-search:-ms-input-placeholder { color: #5f6f81 } |
Большинство браузеров будут скрывать placeholder при клике по полю ввода, что гораздо лучше дает пользователям понять, что это поле ввода. В Chrome такого не наблюдается, поэтому мы воспользуемся этим небольшим трюком для симуляции подобного эффекта, установив цвет placeholder на прозрачный после того, как пользователь кликает по полю ввода:
1 2 3 4 5 6 7 8 |
.gn-search:focus::-webkit-input-placeholder, .no-touch .gn-menu li.gn-search-item:hover .gn-search:focus::-webkit-input-placeholder { color: transparent } input.gn-search:focus { cursor: text } |
При наведении мы изменяем цвет текста ввода на белый, — то же самое, что мы делали на других анкорах (это текст, который вводит пользователь):
1 2 3 |
.no-touch .gn-menu li.gn-search-item:hover input.gn-search { color: white } |
То же самое мы делаем и для текста placeholder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* placeholder */ .no-touch .gn-menu li.gn-search-item:hover .gn-search::-webkit-input-placeholder { color: white } .no-touch .gn-menu li.gn-search-item:hover .gn-search:-moz-placeholder { color: white } .no-touch .gn-menu li.gn-search-item:hover .gn-search::-moz-placeholder { color: white } .no-touch .gn-menu li.gn-search-item:hover .gn-search:-ms-input-placeholder { color: white } |
Когда мы кликаем по иконке поиска, мы на самом деле кликаем по полю ввода, переводя на него фокусировку браузера.
1 2 3 4 5 6 |
.gn-menu-main a.gn-icon-search { position: absolute; top: 0; left: 0; height: 60px; } |
Теперь давайте оформим псевдо элемент ::before для иконок. Мы зададим им параметр inline-block и ширину в 60 пикселей. Нам нужно сбросить все стили шрифта, так как сейчас мы будем использовать наш иконический шрифт, который мы указали в самом начале нашего CSS-кода:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.gn-icon::before { display: inline-block; width: 60px; text-align: center; text-transform: none; font-weight: normal; font-style: normal; font-variant: normal; font-family: 'ecoicons'; line-height: 1; speak: none; -webkit-font-smoothing: antialiased; } |
Давайте определим содержимое для всех иконок:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
.gn-icon-help::before { content: &amp;quot;e000&amp;quot; } .gn-icon-cog::before { content: &amp;quot;e006&amp;quot; } .gn-icon-search::before { content: &amp;quot;e005&amp;quot; } .gn-icon-download::before { content: &amp;quot;e007&amp;quot; } .gn-icon-photoshop::before { content: &amp;quot;e001&amp;quot; } .gn-icon-illustrator::before { content: &amp;quot;e002&amp;quot; } .gn-icon-archive::before { content: &amp;quot;e00d&amp;quot; } .gn-icon-article::before { content: &amp;quot;e003&amp;quot; } .gn-icon-pictures::before { content: &amp;quot;e008&amp;quot; } .gn-icon-videos::before { content: &amp;quot;e009&amp;quot; } |
В целом, нам нужно, чтобы текст анкора отображался рядом с иконкой, но иногда нам нужно бдует просто отобразить иконку. Но нам не нужен просто пустой анкор, в HTML-коде должен быть текст. Поэтому мы оборачиваем эти особые случаи в span-элемент, который затем скрываем, устанавливая ширину и высоту на 0, а свойство overflow на hidden. Почему бы просто не использовать display: none? Скрывание контента таким образом может привести к тому, что его невозможно будет достать через экранные читалки, поэтому давайте удостоверимся в том, что мы не «стираем» что-либо важное для них:
1 2 3 4 5 6 |
.gn-icon span { width: 0; height: 0; display: block; overflow: hidden; } |
Давайте также не забывать о нашей небольшой иконке меню в нашем основном меню. Итак, здесь мы не будем использовать иконку из иконического шрифта, но тем не менее, вы, конечно же, можете это сделать. Вместо этого мы создадим ее при помощи box-shadow, который будет изменять цвета (фоновый цвет и синий цвет), чтобы создать иллюзию 3 линий. Здесь вы по желанию также можете использовать градиент.
1 2 3 4 5 6 7 8 9 |
.gn-icon-menu::before { margin-left: -15px; vertical-align: -2px; width: 30px; height: 3px; background: #5f6f81; box-shadow: 0 3px white, 0 -6px #5f6f81, 0 -9px white, 0 -12px #5f6f81; content: ''; } |
При наведении мы просто инвертируем цвета тени блока:
1 2 3 4 5 |
.no-touch .gn-icon-menu:hover::before, .no-touch .gn-icon-menu.gn-selected:hover::before { background: white; box-shadow: 0 3px #5f6f81, 0 -6px white, 0 -9px #5f6f81, 0 -12px white; } |
А когда на меню стоит выделение (боковое меню открыто), мы добавляем еще больше синего:
1 2 3 4 |
.gn-icon-menu.gn-selected::before { background: #5993cd; box-shadow: 0 3px white, 0 -6px #5993cd, 0 -9px white, 0 -12px #5993cd; } |
Последнее, что нам нужно сделать, это определить два класса для открывания меню и отображения только иконок, а также для отображения всего меню сразу. Давайте назовем этот класс gn-open-part. Другой же класс, gn-open-all, мы применяем либо при клике по иконке основного меню, либо при наведении по области с иконками.
В обоих случаях, нам нужно будет сбросить translate на 0:
1 2 3 4 |
.gn-menu-wrapper.gn-open-all, .gn-menu-wrapper.gn-open-part { transform: translateX(0px); } |
Если мы хотим открыть все меню, нам нужно будет установить правильную ширину:
1 2 3 |
.gn-menu-wrapper.gn-open-all { width: 340px; } |
Открытие полного меню также раскроет и все подменю:
1 2 3 |
.gn-menu-wrapper.gn-open-all .gn-submenu li { height: 60px; } |
Последнее, но не менее важное – это media query, который позволит сделать так, что меню будет использовать всю ширину экрана:
1 2 3 4 5 6 7 8 9 10 |
@media screen and (max-width: 422px) { .gn-menu-wrapper.gn-open-all { transform: translateX(0px); width: 100%; } .gn-menu-wrapper.gn-open-all .gn-scroller { width: 130%; } } |
Мы также настраиваем ширину оболочки скроллинга таким образом, чтобы она была больше 100%. Это не особо важно, так как мы и так не будем видеть скроллинги на большинстве устройств данного размера.
Итак, теперь мы разобрались со стилизацией, и нам нужно добавить некоторый код javascript для логического процесса открывания и закрывания меню (то есть, для применения классов).
Шаг 3. JS
Итак, давайте создадим небольшой скрипт, который позаботится о функционале меню. Когда мы наводим на иконку меню, нам нужно, чтобы первая часть меню выезжала, чтобы мы могли видеть иконки. Если мы наводим на область бокового меню или кликаем по иконке основного меню, тогда перед нами появляется все остальное. Повторное нажатие по иконке меню или нажатие по любой другой области на всей странице приводит к тому, что меню закрывается обратно. Давайте посмотрим, как мы можем все это реализовать.
Мы начинаем с кеширования элементов и инициализации некоторых переменных. Функция bodyClickFn определяет, что происходит, когда меню открыто и мы кликаем по какой-либо обалсти в документе. Нам также следует позаботиться о событиях touch.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
_init : function() { this.trigger = this.el.querySelector( 'a.gn-icon-menu' ); this.menu = this.el.querySelector( 'nav.gn-menu-wrapper' ); this.isMenuOpen = false; this.eventtype = mobilecheck() ? 'touchstart' : 'click'; this._initEvents(); var self = this; this.bodyClickFn = function() { self._closeMenu(); this.removeEventListener( self.eventtype, self.bodyClickFn ); }; } |
Давайте взглянем на события, которые нам нужно инициализировать. Нам нужно открыть первую часть меню (давайте назовем ее меню иконок), когда курсор наведен на иконку-триггер основного меню. Когда мы перемешаем мышь в другую область, это же меню должно закрываться обратно.
1 2 |
this.trigger.addEventListener( 'mouseover', function(ev) { self._openIconMenu(); } ); this.trigger.addEventListener( 'mouseout', function(ev) { self._closeIconMenu(); } ); |
Как только меню иконок окажется в окне просмотра, наведении по нему приведет к открытию полной версии меню. После того, как оно будет отображено, и мы кликнем где-либо в документе, меню обратно закроется. Нам нужно привязать соответствующее событие (click или touchstart) к документу.
1 2 3 4 |
this.menu.addEventListener( 'mouseover', function(ev) { self._openMenu(); document.addEventListener( self.eventtype, self.bodyClickFn ); } ); |
Наконец, если мы кликаем по иконке меню, нам нужно, чтобы все меню было отображено. Нам также нужно привязать соответствующее событие (click или touchstart).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
this.trigger.addEventListener( this.eventtype, function( ev ) { ev.stopPropagation(); ev.preventDefault(); if( self.isMenuOpen ) { self._closeMenu(); document.removeEventListener( self.eventtype, self.bodyClickFn ); } else { self._openMenu(); document.addEventListener( self.eventtype, self.bodyClickFn ); } } ); &amp;lt;p style=&amp;quot;text-align: justify;&amp;quot;&amp;gt;И последнее: нам не нужно, чтобы меню закрывалось, когда мы кликаем где-либо за пределами меню. Так как мы привязываем к документу событие click/touchstart (при закрывании меню), нам нужно следать следующее:&amp;lt;/p&amp;gt; [php]this.menu.addEventListener( this.eventtype, function(ev) { ev.stopPropagation(); } ); |
И здесь предложена последняя функция _initEvents и методы для открывания и закрывания меню.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
_initEvents : function() { var self = this; if( !mobilecheck() ) { this.trigger.addEventListener( 'mouseover', function(ev) { self._openIconMenu(); } ); this.trigger.addEventListener( 'mouseout', function(ev) { self._closeIconMenu(); } ); this.menu.addEventListener( 'mouseover', function(ev) { self._openMenu(); document.addEventListener( self.eventtype, self.bodyClickFn ); } ); } this.trigger.addEventListener( this.eventtype, function( ev ) { ev.stopPropagation(); ev.preventDefault(); if( self.isMenuOpen ) { self._closeMenu(); document.removeEventListener( self.eventtype, self.bodyClickFn ); } else { self._openMenu(); document.addEventListener( self.eventtype, self.bodyClickFn ); } } ); this.menu.addEventListener( this.eventtype, function(ev) { ev.stopPropagation(); } ); }, _openIconMenu : function() { classie.add( this.menu, 'gn-open-part' ); }, _closeIconMenu : function() { classie.remove( this.menu, 'gn-open-part' ); }, _openMenu : function() { if( this.isMenuOpen ) return; classie.add( this.trigger, 'gn-selected' ); this.isMenuOpen = true; classie.add( this.menu, 'gn-open-all' ); this._closeIconMenu(); }, _closeMenu : function() { if( !this.isMenuOpen ) return; classie.remove( this.trigger, 'gn-selected' ); this.isMenuOpen = false; classie.remove( this.menu, 'gn-open-all' ); this._closeIconMenu(); } |
Вот и все. Готово!
Материал взят из зарубежного источника. И представлен исключительно в ознакомительных целях.
Читайте также:
Опубликовал Cooper 20.07.2013 в 11:57, в категории CSS. Вы можете следить за комментариями через RSS 2.0. Вы можете перейти в конец записи и оставить комментарий. Пинги запрещены. |