Книжка с пробниками на CSS3 и jQuery
В данном уроке мы создадим книжку с пробниками с помощью трансформаций вращения CSS и JavaScript. Будет формироваться структура, похожая на набор пробников, в которой панели воспринимают нажатия кнопки мыши, после чего происходит открытие нужной страницы.
Разметка HTML
Разметка будет иметь простую структуру с несколькими элементами div, каждый из которых содержит элемент span для иконки и заголовок h4:
Последний элемент div не содержит иконку, но имеет вместо нее элементы h4 и h5. Он будет служить "обложкой" нашей книжке с пробниками.
CSS
Сначала определим стили для контейнера. Сделаем его такой же ширины, как и отдельная панель (хотя они и будут занимать больше места), чтобы можно было легко центрировать:
Наша цель - построить структуру в виде книжки с несколькими панелями. Каждая панель будет вращаться с помощью трансформаций CSS. Поэтому установим для панели ширину, высоту и зададим абсолютное позиционирование. В начальном положении все панели "сложены" в стопку. Свойство transform-origin определяет как будут вращаться наши панели. Так как мы будем использовать нижний левый угол, то установим значения 25% 90%. Свойство backface-visibility: hidden помогает скрыть лишний текст при вращении:
Определим разные цвета фона и тени для панелей:
Для придания большей реалистичности "верхним" верхним элементам устанавливаем еле заметную тень, увеличивая ее яркость при смещении по структуре.
Последняя панель будет работать как обложка, поэтому для нее установим специальный фон и тень:
Установим изображение маленькой бронзовой клепки. Для нее будем использовать псевдо-класс :after. Он будет иметь градиент и тень, чтобы придать клепке реалистичности. Положение зависит от свойства transform-origin панели, поэтому устанавливаем клепку в левый нижний угол:
Определим стили для заголовков
Большой заголовок на обложке будет повернут и размещен в правильное положение. Значения зависят от размера надписи, поэтому при изменении текста нужно будет провести настройку положения.
В завершении установим стили для иконок. В нашем проекте используется специальный шрифт для генерации иконок Fontello . Определим стили для элемента span и псевдо-элемента :before, который содержит символы шрифта для иконок:
JavaScript
Рассмотрим опции, которые будет иметь наш плагин:
Вот их назначение:
center: индекс центральной панели, которая будет иметь угол 0 градусов при открытии книжки
angleInc: угол (в градусах) между панелями
speed и easing: скорость и функция перехода между панелями
proximity: угол между открытой панелью и ее соседями
neighbor: угол между элементами при открытой панели
onLoadAnim: если установлено значение true, книжка будет открываться с анимацией при загрузке, в другом случае - книжка будет сразу выводиться открытой
initclosed: если установлено значение true, книжка будет изначально закрыта
closeIdx: индекс пункта, который запускает функцию открытия/закрытия книжки
Запуск начинается с выполнения функции _init:
Мы кешируем некоторые элементы и инициализируем переменные, которые будут использоваться позже. Также нужно проверить начальное состояние книжки. Если она должна быть изначально закрыта нужно установить переходы CSS для пунктов (функция _setTransition).
В завершении загружаем события для пунктов.
Когда происходит нажатие на пункте, панель вращается до 0 градусов. Также вращаются соседние панели чтобы открылось содержание:
Функция _rotateSiblings имеет вид:
Соседние панели поворачиваются так, чтобы оставлять пространство для обзора содержания. А их положение определяется опциями proximity и neighbor.
Если изначально книжка должна быть закрыта и/или мы определяем пункт, который будет запускать открытие/закрытие, то событие click на указанном пункте будет производиться соответствующие действия в зависимости от текущего статуса:
Разметка 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>
<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;
}
position: relative;
width: 150px;
height: 400px;
margin: 30px auto 0 auto;
}
Наша цель - построить структуру в виде книжки с несколькими панелями. Каждая панель будет вращаться с помощью трансформаций CSS. Поэтому установим для панели ширину, высоту и зададим абсолютное позиционирование. В начальном положении все панели "сложены" в стопку. Свойство 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;
}
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);
}
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);
}
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);
}
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);
}
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;
}
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' */
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 : 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();
}
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
} );
}
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 );
}
} );
}
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
} );
} );
}
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;
}
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;
}
-
FalleN -
3338 -
1 -
194
Но только нужно ставить наверное на те модули которые обновляться должны не раньше через пару тройку минут
С Уважением, Андрей...