9.08.13, 09:21
0 комментарий
  Уроки

Меню в стиле Google Nexus

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

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

Приступаем!

Разметка

Наше меню будет состоять их 2 основных элементов: основное меню – то, которое вы можете видеть вверху страницы в виде шапки сайта, и боковое меню. Первому меню мы зададим класс 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="http://tympanus.net/codrops">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-код

Обратите внимание на то, что CSS не содержит браузерных префиксов, но в исходный файлах они присутствуют.

Давайте начнем с указания border-box для всех свойств box-sizing:

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


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

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


Здесь представлены базовые (reset) стили для списков и подсписков.

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

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


Давайте также определим стили при наведении курсора мыши, в которых мы инвертируем цвета. Для первого пункта бокового меню, которым будет наш поиск, нам понадобится отдельный стиль при наведении. Здесь у нас не будет анкора, который будет заполнять пункт списка, поэтому давайте определим hover-стиль для li и укажем, что будет происходить с иконкой (анкором) и самим элементом li:

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


Первый пункт списка будет представлять собой специальный trigger-пункт, и так как нам нужно скрыть текст и использовать псевдо-элемент для иконки меню, мы установим свойство user-select на none, а свойству ширины (width) зададим то же значение, что указано в высоте всего пункта.

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


Давайте удалим выравнивания при помощи следующего миниатюрного хака clearfix от Nicolas Gallagher:

.gn-menu-main:after {
    display: table;
    clear: both;
    content: '';
}


Итак, на данный момент у нас отсутствуют стили для иконки меню, но давайте оставим это на потом – мы применим их после того, как определим другие псевдо-классы иконки.

Давайте перейдем к оболочке бокового меню. Зачем нужны дополнительные оболочки? Что же, если вас не смущает не нужная полоса прокрутки, то можете просто опустить эти стили, и просто указать overflow-y: scroll. Но так как полоса прокрутки действительно преломляет наш минималистский дизайн в браузерах Windows, нам нужно воспользоваться этим небольшим трюком. Мы выставляем свойство overflow оболочки на hidden, с определенной шириной (изначально этой ширины достаточно для того, чтобы отобразить иконки). Затем мы задаем оболочке скроллинга большую ширину и высоту равную 100%. Полоса прокрутки будет скрыта. Затем наше меню будет расширяться до необходимой высоты, и его можно будет прокручивать.
Изначально нам нужно скрыть меню, поэтому мы зададим ему значение negative left (равное его ширине). Почему мы не воспользуемся здесь параметром 2d translate?

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


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

.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, поэтому мы зададим ему прозрачный цвет фона и сделаем так, чтобы placeholder-элементы выглядели как обычные пункты меню:

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 на прозрачный после того, как пользователь кликает по полю ввода:

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


То же самое мы делаем и для текста placeholder:

/* 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
}


Анкор иконки поиска будет особенным, так как нам не нужно отображать текст рядом с ним. Весь пункт списка будет представлять собой блок. Видите – задавая анкору иконки абсолютное позиционирование, мы делаем так, что ввод поискового запроса начинается с самой левой части пункта списка. Но помните, мы задаем полю ввода большой отступ слева, что позволяет нам добиться того, что вводимый текст появляется уже после иконки поиска. Когда мы кликаем по иконке поиска, мы на самом деле кликаем по полю ввода, переводя на него фокусировку браузера.

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


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

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


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

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


В целом, нам нужно, чтобы текст анкора отображался рядом с иконкой, но иногда нам нужно бдует просто отобразить иконку. Но нам не нужен просто пустой анкор, в HTML-коде должен быть текст. Поэтому мы оборачиваем эти особые случаи в span-элемент, который затем скрываем, устанавливая ширину и высоту на 0, а свойство overflow на hidden. Почему бы просто не использовать display: none? Скрывание контента таким образом может привести к тому, что его невозможно будет достать через экранные читалки, поэтому давайте удостоверимся в том, что мы не «стираем» что-либо важное для них:

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


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

.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, мы применяем либо при клике по иконке основного меню, либо при наведении по области с иконками.
В обоих случаях, нам нужно будет сбросить translate на 0:

.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 query, который позволит сделать так, что меню будет использовать всю ширину экрана:

@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 для логического процесса открывания и закрывания меню (то есть, для применения классов).

javascript-код

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

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

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


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

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 и методы для открывания и закрывания меню.

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


И на этом все! Спасибо за чтение, и надеемся, что вам понравилось данное руководство!

Исходники можно посмотреть в архиве!

СКАЧАТЬ

Вес файла
51.24 Kb

Реклама

Напишите своё мнение

  • acuteaggressiveair_kissangel
    badbbbeachbeee
    biggrinbig_bossblumblush
    boastbombboredombye
    clappingcraycrazycurtsey
    dancedashdeclarediablo
    diroldon-t_mentiondownloaddrinks
    first_moveflirtfocusfool
    friendsgive_heartgive_rosegood
    hangheartheathelp
    hihunterhystericireful
    kingkisslaughlazy
    lolmail1mambaman_in_love
    mdamega_shokmoilmosking
    musicneanegativenew_russian
    okon_the_quietpardonparting
    partypilotpioneerpleasantry
    popcormpranksterprevedpunish
    roflrtfmrussiansad
    sarcasticscarescratchsearch
    secretsensoredshokshout
    slowsmilesmokesoldier
    soldier_girlsorryspitefulspruce_up
    stinkersuicidesunsuperstition
    swoonteasetenderthanks
    thisto_pick_ones_noseto_take_umbragetreaten
    umnikunknwvampirevava
    victorywackowhistlewink
    wizardyahooyesyu
    e101e102e103e104
    e105e106e107e108
    e109e110e111e112
    e113e114e115e116
    e117e118e119e120
    e121e122e123e124
    e125e126e127e128
    e129e130e131e132
    e133e134e135e136
    e137e138e139e140
    e141e142e143e144
    e145e146e147e148
    e149e150e151e152
    e153e154e155e156
    e157e158e159e160
    e161e162e163e164
    e165e166e167e168
    e169e170e171e172
    e173e174e175e176
    e177e178e179e180
    e181e182e183e184
    e185e186e187e188
    e189e190e191e192
    e193e194e195e196
    e197e198e199e200
    e201e202e203e204