Книжка с пробниками на CSS и jQuery / CSS transform

В данном уроке мы создадим книжку с пробниками при помощи CSS transform и JavaScript. Сформируем структуру, в ней будет находиться набор пробников (панелей), при нажатии по которым, открывается нужная страница.

Сложность

HTML Разметка

Разметка будет иметь простую структуру с несколькими элементами div, каждый из которых содержит элемент span для иконки и заголовок h4:

<div id="sb-container" class="sb-container">
 <div>
<span class="sb-icon icon-cog"></span>
<h4><span>Настройка</span></h4>
</div>

<div>
<span class="sb-icon icon-flight"></span>
<h4><span>Режим пользователя</span></h4>
</div>

<div>
. . .
</div>

. . . <div>
<h4><span>Профиль</span></h4>
<h5><span>Выбираем закладку</span></h5>
</div>

</div><!-- sb-container -->

Последний элемент div не содержит иконку, но имеет вместо нее элементы h4 и h5. Он будет служить в качестве обложки нашей книги с пробниками.

CSS

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

.sb-container {
    position: relative;
    width: 150px;
    height: 400px;
    margin: 30px auto 0 auto;
}

Наша цель - построить структуру в виде книги с несколькими панелями. Каждая панель будет вращаться с помощью CSS transform rotate. Поэтому установим для каждой панели ширину, высоту и зададим абсолютное позиционирование. В начальном положении все панели "сложены" в стопку. Свойство transform-origin определяет как будут вращаться панели. Так как мы будем использовать нижний левый угол в качестве опорной точки, то установим значения 25% 90%. Свойство backface-visibility: hidden помогает скрыть лишний текст при вращении:

.sb-container div {
    position: absolute;
    top: 0;
    left: 0; 
    width: 130px;
    background: #fff;
    height: 400px;
    border-radius: 5px;
    cursor: pointer;
    text-align: center;
    background-image: url(../images/fabric.png);
    transform-origin: 25% 90%;
    backface-visibility: hidden;
}

Определим различные цвета фона и тени для панелей:

.sb-container div:nth-child(1){
    background-color: #ea2a29;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 1px 1px 1px rgba(0,0,0,0.1);
}
.sb-container div:nth-child(2){
    background-color: #f16729;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 2px 2px 1px rgba(0,0,0,0.1);
}
.sb-container div:nth-child(3){
    background-color: #f89322;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 3px 3px 2px rgba(0,0,0,0.2);
}
.sb-container div:nth-child(4){
    background-color: #ffcf14;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 4px 4px 4px rgba(0,0,0,0.2);
}
.sb-container div:nth-child(5){
    background-color: #ffea0d;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 5px 5px 6px rgba(0,0,0,0.3);
}
.sb-container div:nth-child(6){
    background-color: #87b11d;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 6px 6px 8px rgba(0,0,0,0.3);
}
.sb-container div:nth-child(7){
    background-color: #008253;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 7px 7px 10px rgba(0,0,0,0.4);
}
.sb-container div:nth-child(8){
    background-color: #3277b5;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 8px 8px 12px rgba(0,0,0,0.4);
}
.sb-container div:nth-child(9){
    background-color: #4c549f;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 9px 9px 14px rgba(0,0,0,0.4);
}
.sb-container div:nth-child(10){
    background-color: #764394;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 10px 10px 16px rgba(0,0,0,0.4);
}
.sb-container div:nth-child(11){
    background-color: #ca0d86;
    box-shadow: -1px -1px 3px rgba(0,0,0,0.1), 11px 11px 18px rgba(0,0,0,0.4);
}

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

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

.sb-container div:last-child{
    background: #645b5c url(../images/cover.jpg) repeat center center;
    box-shadow: 
        -1px -1px 3px rgba(0,0,0,0.2),
        12px 12px 20px rgba(0,0,0,0.6),
        inset 2px 2px 0 rgba(255, 255, 255, 0.1);
}

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

.sb-container div:last-child:after{
    content: '';
    position: absolute;
    bottom: 15px;
    left: 15px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #dddddd;
    background: linear-gradient(135deg, #dddddd 0%,#58535e 48%,#889396 100%);
    box-shadow: -1px -1px 1px rgba(0,0,0,0.5), 1px 1px 1px rgba(255,255,255,0.1);
}

Определим стили для заголовков

.sb-container div h4{
    color: rgba(255,255,255,0.9);
    text-shadow: 1px 1px 1px rgba(0,0,0,0.2);
    font-weight: 700;
    font-size: 16px;
    text-transform: uppercase;
    border-top: 1px dashed rgba(0,0,0,0.1);
    border-bottom: 1px dashed rgba(0,0,0,0.1);
    margin: 5px;
    padding: 5px;
    user-select: none;
}
.sb-container div:last-child h4{
    background: rgba(0,0,0,0.2);
    box-shadow: 0 1px 1px rgba(255,255,255,0.1);
}

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

.sb-container div:last-child h5{
    font-size: 50px;
    white-space: nowrap;
    text-align: left;
    margin: 0;
    padding: 0;
    position: absolute;
    line-height: 50px;
    top: 0px;
    left: 0px;
    color: #111;
    text-shadow: -1px -1px 1px rgba(255,255,255,0.1);
    text-transform: uppercase;
    transform: rotate(-90deg) translateX(-157%) translateY(73px);
    transform-origin: 0 0;
    user-select: none;
}

Установим стили для иконок. В нашем проекте используется специальный шрифт с иконками Fontello. Определим стили для элемента span и псевдо-элемента :before, который содержит символы шрифта для иконок:

span.sb-icon{
    display: block;
    height: 70px;
    width: 70px;
    margin: 20px auto;
    user-select: none;
}
span.sb-icon:before {
    font-family: 'icons';
    font-style: normal;
    font-weight: normal;
    speak: none;
    display: block;
    text-decoration: inherit;
    text-align: center;
    text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); 
    line-height: 64px;
    width: 100%;
    font-size: 60px;
    color: #000;
    text-shadow: 0 0 1px #000;
 
}
.icon-cog:before { content: '\35'; } /* '5' */
.icon-flight:before { content: '\37'; } /* '7' */
.icon-eye:before { content: '\34'; } /* '4' */
.icon-install:before { content: '\39'; } /* '9' */
.icon-bag:before { content: '\36'; } /* '6' */
.icon-globe:before { content: '\38'; } /* '8' */
.icon-picture:before { content: '\32'; } /* '2' */
.icon-video:before { content: '\30'; } /* '0' */
.icon-download:before { content: '\41'; } /* 'A' */
.icon-mobile:before { content: '\42'; } /* 'B' */
.icon-camera:before { content: '\33'; } /* '3' */

JavaScript

Рассмотрим опции, которые будет иметь наш плагин:

$.SwatchBook.defaults   = {
    center      : 6,
    angleInc    : 8,
    speed       : 700,
    easing      : 'ease',
    proximity   : 45,
    neighbor    : 4,
    onLoadAnim  : true,
    initclosed  : false,
    closeIdx    : -1
};
  • center: номер центральной панели, которая будет иметь угол 0 градусов при открытии книжки
  • angleInc: угол (в градусах) между панелями
  • speed и easing: скорость и функция перехода между панелями
  • proximity: угол между открытой панелью и ее соседями
  • neighbor: угол между элементами при открытой панели
  • onLoadAnim: если установлено значение true, книжка будет
  • открываться с анимацией при загрузке, в другом случае - книжка будет сразу выводиться открытой
  • initclosed: если установлено значение true, книжка будет изначально закрыта
  • closeIdx: индекс пункта, который запускает функцию открытия/закрытия книжки

Запускаем с выполнения функции _init:

_init           : function( options ) {
             
    this.options    = $.extend( true, {}, $.SwatchBook.defaults, options );
 
    this.$items     = this.$el.children( 'div' );
    this.itemsCount = this.$items.length;
    this.current    = -1;
    this.support    = Modernizr.csstransitions;
    this.cache      = [];
     
    if( this.options.onLoadAnim ) {
 
        this._setTransition();
 
    }
 
    if( !this.options.initclosed ) {
 
        this._center( this.options.center, this.options.onLoadAnim );
 
    }
    else {
 
        this.isClosed   = true;
        if( !this.options.onLoadAnim ) {
 
            this._setTransition();
 
        }
 
    }
     
    this._initEvents();
     
}

Некоторые элементы добавим в кэш и инициализируем переменные. Также необходимо проверить начальное состояние книги. Если она должна быть изначально закрыта, установим CSS переходы для панелей (функция _setTransition).

В завершении загружаем события для пунктов.

_setTransition  : function() {
 
    if( !this.support ) {
 
        return false;
 
    }
 
    this.$items.css( {
        '-webkit-transition': '-webkit-transform ' + this.options.speed + 'ms ' + this.options.easing,
        '-moz-transition'   : '-moz-transform ' + this.options.speed + 'ms ' + this.options.easing,
        '-o-transition'     : '-o-transform ' + this.options.speed + 'ms ' + this.options.easing,
        '-ms-transition'    : '-ms-transform ' + this.options.speed + 'ms ' + this.options.easing,
        'transition'        : 'transform ' + this.options.speed + 'ms ' + this.options.easing
    } );
 
}

При нажатии по панели, она вращается до 0 градусов. Также вращаются все соседние панели чтобы открылось содержание:

_initEvents     : function() {
 
    var _self = this;
 
    this.$items.on( 'click.swatchbook', function( event ) {
 
        var $item   = $( this ),
            itmIdx  = $item.index();
 
        if( itmIdx === _self.current ) {
 
            return false;
 
        }
 
        if( _self.options.closeIdx !== -1 && itmIdx === _self.options.closeIdx ) {
 
            _self._openclose();
            _self._setCurrent();
 
        }
        else {
 
            _self._setCurrent( $item );
 
            var transformStr    = 'rotate(0deg)';
 
            $item.css( {
                '-webkit-transform' : transformStr,
                '-moz-transform'    : transformStr,
                '-o-transform'      : transformStr,
                '-ms-transform'     : transformStr,
                'transform'         : transformStr
            } );
 
            _self._rotateSiblings( $item );
 
        }
 
    } );
 
}

Функция _rotateSiblings имеет вид:

_rotateSiblings : function( $item ) {
 
    var _self       = this,
        idx         = $item.index(),
        $cached     = this.cache[ idx ],
        $siblings;
 
    if( $cached ) {
 
        $siblings = $cached;
 
    }
    else {
 
        $siblings = $item.siblings();
        this.cache[ idx ] = $siblings;
 
    }
 
    $siblings.each( function( i ) {
 
        var rotateVal   = ( i < idx ) ?
                            _self.options.angleInc * ( i - idx ) :
                            ( i - idx === 1 ) ? _self.options.proximity : _self.options.proximity + ( i - idx - 1 ) * _self.options.neighbor;
 
        var transformStr    = 'rotate(' + rotateVal + 'deg)';
 
        $( this ).css( {
            '-webkit-transform' : transformStr,
            '-moz-transform'    : transformStr,
            '-o-transform'      : transformStr,
            '-ms-transform'     : transformStr,
            'transform'         : transformStr
        } );
 
    } );
 
}

Соседние панели поворачиваются так, чтобы осталось пространство для отображения содержания. А их положение определяется опциями proximity и neighbor.

Если изначально книга должна быть закрыта, мы определяем пункт, который будет запускать открытие/закрытие, событие click на указанном пункте будет выполнять соответствующие действия в зависимости от текущего статуса:

_openclose      : function() {
 
    var _self = this;
 
    if( this.isClosed ) {
 
        this._center( this.options.center, true );
 
    }
    else {
 
        this.$items.each( function( i ) {
 
            var transformStr    = 'rotate(0deg)';
 
            $( this ).css( {
                '-webkit-transform' : transformStr,
                '-moz-transform'    : transformStr,
                '-o-transform'      : transformStr,
                '-ms-transform'     : transformStr,
                'transform'         : transformStr
            } );
 
        } );
 
    }
 
    this.isClosed = !this.isClosed;
 
}

Перевод статьи Swatch Book with CSS3 and jQuery

Тэги: transform

Вход

Уважаемый пользователь! Мы обнаружили, что вы используете AdBlock и вынуждены скрыть часть материалов на нашем сайте. Siteacademy существует и развивается за счет доходов от рекламы. Просим внести наш сайт в список исключений или отключить Блокировщик рекламы на нашем сайте.