Lecaw

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

Создание меню в стиле Google Nexus

August 13, 2013
CSS
203hits

Сегодня я хочу показать вам, как создать боковую панель меню в стиле Google Nexus 7. Меню выдвигается с действительно хорошим эффектом, где некоторые подпункты расширяются. Если навести курсор на специальный значок меню, иконки на боковой панели раскроются. При нажатии на саму иконку, все боковое меню полностью откроется. Первый пункт выполнен в качестве строки поиска.

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

 

Меню будет состоять из обычного неупорядоченного списка, вложенных списков и некоторых CSS переходов. Мы будем использовать JavaScript для смены классов эффектов, обработки наведения курсора и щелчка. С помощью медиа запросов, мы отрегулируем размер меню для небольших экранов.

HTML разметка

Наше меню будет состоять из двух основных частей: главное меню, то которое находится вверху страницы, в качестве заголовка, и боковое меню. Для главного меню установим класс “gn-menu-main”, а боковое меню установим в элементе nav. Конечно, вы можете использовать любую другую структуру на свое усмотрение.

Первый пункт меню будет содержать иконку меню и элементы nav:

<ul id="gn-menu" 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>

Внутри элемента nav мы добавим еще одну оболочку, с помощью которой мы скроем лишнюю прокрутку для браузеров под управлением Windows. Но и конечно основа меню это - неупорядоченный список с классом “gn-menu”. Первый пункт меню будет в качестве строки поиска:

<div class="gn-scroller">
    <ul class="gn-menu">
        <li class="gn-search-item">
            <input placeholder="Search" type="search" class="gn-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>
</div><!-- /gn-scroller -->

CSS

Для начала установим границы окна для всех box-sizing:

*,
*:after,
*::before {
    box-sizing: border-box;
}

Так как мы будем использовать шрифт-иконки, нам понадобится приложение IcoMoon. Я взял иконки из набора Matthew Skiles’ Eco Ico.

@font-face {
    font-weight: normal;
    font-style: normal;
    font-family: 'ecoicons';
    src: url("../fonts/ecoicons/ecoicons.eot");
    src: url("../fonts/ecoicons/ecoicons.eot?#iefix") format("embedded-opentype"), url("../fonts/ecoicons/ecoicons.woff") format("woff"), url("../fonts/ecoicons/ecoicons.ttf") format("truetype"), url("../fonts/ecoicons/ecoicons.svg#ecoicons") format("svg");
}

Для того чтобы установить эти иконки мы будем использовать псевдо-элементы.

Но сначала сбросим стиль списка по умолчанию:

.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;
}

Теперь, давайте зададим стили для основного списка. Он будет закреплен в верхней части страницы, а также установим высоту в 60px:

.gn-menu-main {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 60px;
    font-size: 13px;
}

Общий стиль для всех ссылок в нашем меню и подменю будет следующим:

.gn-menu-main a {
    display: block;
    height: 100%;
    color: #5f6f81;
    text-decoration: none;
    cursor: pointer;
}

Давайте также определим некоторые стили при наведении, где мы инвертируем цвета. Для первого элемента бокового меню, нам понадобится особый стиль при наведении.

.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;
}

Дочерние элементы списка будут плавающими, а также будут иметь рамку справа:

.gn-menu-main > li {
    display: block;
    float: left;
    height: 100%;
    border-right: 1px solid #c6d0da;
    text-align: center;
}

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

.gn-menu-main li.gn-trigger {
    position: relative;
    width: 60px;
    user-select: none;
}

Последний элемент в главном списке будет перемещен вправо:

.gn-menu-main > li:last-child {
    float: right;
    border-right: none;
    border-left: 1px solid #c6d0da;
}

Главное меню будет иметь небольшие отступы, а также мы поменяем стиль текста:

.gn-menu-main > li > a {
    padding: 0 30px;
    text-transform: uppercase;
    letter-spacing: 1px;
    font-weight: bold;
}

Давайте перейдем к оболочке бокового меню. Зачем нам еще одна оболочка? Ну, если вам нужна полоса прокрутки, Вы можете просто добавить свойство overflow-y: scroll для меню. Но так как нам не нужна полоса прокрутки, мы используем маленькую хитрость, чтобы скрыть её. Мы установим основную оболочку, со свойством overflow: hidden, определенной ширины. Затем мы установим полосу прокрутки в отдельную оболочку с ещё большей шириной и высотой в 100%. Таким образом полоса прокрутки будет скрыта. Наше меню будет расположено во всю ширину экрана и его можно будет прокручивать.
Изначально мы хотим скрыть меню, поэтому мы зададим для него отрицательное левое значение (ширины).

.gn-menu-wrapper {
    position: fixed;
    top: 60px;
    bottom: 0;
    left: 0;
    overflow: hidden;
    width: 60px; 
    border-top: 1px solid #c6d0da;
    background: white;
    transform: translateX(-60px); 
    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;
}

Давайте добавим тень для разделения элементов списка. Это поможет нам избежать двойных линий, скрывая пункты подменю:

.gn-menu li:not(:first-child),
.gn-menu li li {
    box-shadow: inset 0 1px #c6d0da
}

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

.gn-submenu li {
    overflow: hidden;
    height: 0;
    transition: height 0.3s;
}

Цвет будет немного светлее, чем у родительских элементов меню:

.gn-submenu li a {
    color: #c1c9d1
}

Теперь, давайте перейдем к стилю строки поиска. Мы хотим сделать его действительно тонким, как на странице Google Nexus, поэтому мы зададим для него прозрачные фоновые цвета:

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;
}
 
 
.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
}

Как только пользователь нажимает на поле ввода и фокусирует его, рамка станет будет прозрачной:

.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
}

При наведении мы поменяем цвет текста на белый:

.no-touch .gn-menu li.gn-search-item:hover input.gn-search {
    color: white
}

Цвет заполнителя:

.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
}

При нажатии на иконку поиска мы будем фокусировать поле ввода:

.gn-menu-main a.gn-icon-search {
    position: absolute;
    top: 0;
    left: 0;
    height: 60px;
}

Теперь, давайте перейдем к стилю псевдо-элемента ::before для иконок. Мы сделаем его в качестве inline-block и зададим ширину 60px.

.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;
}

Давайте определим content для всех иконок:

gn-icon-help::before {
    content: "\e000"
}
 
.gn-icon-cog::before {
    content: "\e006"
}
 
.gn-icon-search::before {
    content: "\e005"
}
 
.gn-icon-download::before {
    content: "\e007"
}
 
.gn-icon-photoshop::before {
    content: "\e001"
}
 
.gn-icon-illustrator::before {
    content: "\e002"
}
 
.gn-icon-archive::before {
    content: "\e00d"
}
 
.gn-icon-article::before {
    content: "\e003"
}
 
.gn-icon-pictures::before {
    content: "\e008"
}
 
.gn-icon-videos::before {
    content: "\e009"
}

Нам нужно показать текст привязки рядом с иконкой, но в некоторых случаях нам нужна только иконка. Для этих особых случаев, мы просто спрячем элемент span, установим его ширину и высоту в 0.

.gn-icon span {
    width: 0;
    height: 0;
    display: block;
    overflow: hidden;
}

Давайте не будем забывать о нашей маленькой иконке меню. Итак, здесь мы не будем использовать шрифт-иконку. Вместо этого мы создадим иконку с box-shadow, которая будет изменять цвет, чтобы создать три линии. Вы также можете использовать градиент.

.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: '';
}

При наведении, мы инвертируем цвета тени:

.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;
}

И когда меню открыто, мы сделаем иконку более синей:

.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.

.gn-menu-wrapper.gn-open-all,
.gn-menu-wrapper.gn-open-part {
    transform: translateX(0px);
}

Если нам нужно открыть меню полностью, необходимо установить правильную ширину:

.gn-menu-wrapper.gn-open-all {
    width: 340px;
}

Следует также расширить пункты подменю:

.gn-menu-wrapper.gn-open-all .gn-submenu li {
    height: 60px;
}

И последнее, но не менее важное – медиа запросы, с помощью которых меню будет отображаться на весь экран:

@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%;
    }
}

Также скорректируем ширину оболочки прокрутки. Конечно это не столь важно, поскольку мы не видим полосы прокрутки на мобильных устройствах.

JavaScript

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

Начнем с кэширования некоторых элементов и инициализации переменных. Функция bodyClickFn определяет, что происходит, когда меню открыто и когда мы щелкаем где-то в другом месте документа.

_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 );
    };
}

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

this.trigger.addEventListener( 'mouseover', function(ev) { self._openIconMenu(); } );
this.trigger.addEventListener( 'mouseout', function(ev) { self._closeIconMenu(); } );

Для открытия и закрытия меню, мы должны связать соответствующее события (click или touchstart) с документом.

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 );
    }
} );

И еще один момент, нам не нужно чтобы меню закрывалось, если мы щелкаем где-нибудь в области меню. Так как мы связываем события click/touchstart с документом, мы должны сделать следующее:

this.menu.addEventListener( this.eventtype, function(ev) { ev.stopPropagation(); } );

Вот что у нас получилось:

_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();
}

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

 

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

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

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

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