Дек
22
2012

Перелистывающиеся страницы, используя BookBlock

Fullscre

Руководство о том, как сделать полноэкранный html макет с перелистывающимися страницами, используя BookBlock. Идея в том, что страницы переворачиваются как в книге, а доступ к главам осуществляется через меню в сайдбаре слева.
Мы получили несколько вопросов о том, как использовать BookBlock во весь экран. Поэтому, мы решили сделать полноэкранный макет, применить BookBlock и добавить сайдбар с меню. Мы покажем вам, как изменить BookBlock и использовать некоторые опции для навигации по контенту.
Fullscre

ДемоИсходники

Идея в навигации по страницам используя стрелки или перелистывая страницы мышью, а еще выдвигается меню после нажатия на кнопку. Меню в сайдбаре содержит ссылки на разные страницы, выполняя роль оглавления. После нажатия на ссылку в этом меню — открывается соответствующая страница.

Мы также используем jScrollPane для добавления своего скролбара, появляющегося при необходимости.

Обратите внимание: 3D CSS трансформации будут работать только в современных браузерах, которые поддерживают эти свойства. Например Opera 12 их не понимает.

Демо содержит выдержки из веселой книги «The Funny Side of Physic» автор A. D. Crabtre.

Мы использовали следующие библиотеки и jQuery плагины:

  1. BookBlock, автор Pedro Botelho
  2. Custom jQuery++, автор Bitovi
  3. jScrollPane, автор Kevin Luck
  4. jQuery Mouse Wheel Plugin, автор Brandon Aaron
  5. Измененный Modernizr (что включено смотрите внутри)

Итак, начнем!

Разметка

Создадим основной контейнер для всех элементов Внутри мы разместим div’ы под сайдбар, с классом «menu-panel» и обертку BookBlock с классом «bb-custom-wrapper». BookBlock будет содержать обертку (к которой мы будем применять плагин) и структуру необходимую для плагина. Внутри каждой страницы мы добавим обертку и div необходимый для нашего измененного скролбара:

<div id="container" class="container">	

	<div class="menu-panel">
		<h3>Table of Contents</h3>
		<ul id="menu-toc" class="menu-toc">
			<li class="menu-toc-current"><a href="#item1">Self-destruction</a></li>
			<li><a href="#item2">Why we die</a></li>
			<li><a href="#item3">The honeymoon</a></li>
			<li><a href="#item4">A drawing joke</a></li>
			<li><a href="#item5">Commencing practice</a></li>
		</ul>
	</div>

	<div class="bb-custom-wrapper">

		<div id="bb-bookblock" class="bb-bookblock">

			<div class="bb-item" id="item1">
				<div class="content">
					<div class="scroller">
						<h2>Self-destruction</h2>
						<p>...</p>
					</div>
				</div><!-- /content -->
			</div><!-- /bb-item -->

			<div class="bb-item" id="item2"><!-- ... --></div>

			<div class="bb-item" id="item3"><!-- ... --></div>

			<div class="bb-item" id="item4"><!-- ... --></div>

			<div class="bb-item" id="item5"><!-- ... --></div>

		</div><!-- /bb-bookblock -->

		<nav>
			<a id="bb-nav-prev" href="#">�</a>
			<a id="bb-nav-next" href="#">→</a>
		</nav>

		<span id="tblcontents" class="menu-button">Table of Contents</span>

	</div><!-- /bb-custom-wrapper -->

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

Ссылки меню ведут на соответствующие страницы BookBlock’а (bb-item). Мы также добавили кнопки в виде стрелок и кнопку открытия\закрытия меню.

Теперь пора заняться стилями.

CSS

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

Мы не касаемся здесь стилей самого плагина BookBlock (вы можете найти их в файле bookblock.css), вместо этого мы сфокусируемся на стилях важных для макета и еще пары вещей.

Для начала импортируем шрифт Lato из Google web fonts:

@import url(http://fonts.googleapis.com/css?family=Lato:300,400,700);

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

html { 
	height: 100%; 
}

Мы используем border-box для box-sizing, что позволит нам задавать ширину и высоту элементов в процентах, при том что мы используем padding’и, и при этом не нужно волноваться о превышении размеров.

*,
*:after,
*:before {
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
	padding: 0;
	margin: 0;
}

(Мы просто сбрасываем padding’и и margin’ы для всех элементов; если вы не хотите этого делать — просто нормализуйте соответствующие элементы)

Определим стиль шрифта для body и зададим ему высоту 100%. Помните, это необходимо потому что потомки будут абсолютные и с высотой 100%:

body {
	font-family: 'Lato', Calibri, Arial, sans-serif;
	font-weight: 400;
	font-size: 100%;
	color: #333;
	height: 100%;
}

Мы используем Modernizr и добавили класс «no-js» для элемента html. Когда JavaScript включен, Modernizr заменит этот класс на «js». Это позволит нам задать определенные CSS свойства для элементов, на случай если JavaScript выключен. 100% ширины и высоты имеет смысл только при включенном JS и только когда мы хотим применить к body overflow hidden:

.js body {
	overflow: hidden;
}

Определим пару стилей для ссылок:

a {
	color: #555;
	text-decoration: none;
}

a:hover {
	color: #000;
}

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

Итак, применим 100% ширину и высоту к нашей основной обертке и добавим transition для контейнера:

.container,
.bb-custom-wrapper,
.bb-bookblock {
	width: 100%;
	height: 100%;
}

.container {
	position: relative;
	left: 0px;
	transition: left 0.3s ease-in-out;
}

Когда мы нажимаем на кнопку меню, мы добавляем новый класс контейнеру, который устанавливает left в занчение 240 px (ширина сайдбара):

.slideRight {
	left: 240px;
}

При отключенном JS, нам не нужно этого, поэтому мы просто добавим левый padding:

.no-js .container {
	padding-left: 240px;
}

По умолчанию мы хотим, чтобы сайдабар был закреплен слева:

.menu-panel {
	background: #f1103a;
	width: 240px;
	height: 100%;
	position: fixed;
	z-index: 1000;
	top: 0;
	left: 0;
	text-shadow: 0 1px 1px rgba(0,0,0,0.1);
}

Когда JS включен, мы устанавливаем позицию как абсолютную и left равный -240px:

.js .menu-panel {
	position: absolute;
	left: -240px;
}

Добавим стили для элементов меню:

.menu-panel h3 {
	font-size: 1.8em;
	padding: 20px;
	font-weight: 300;
	color: #fff;
	box-shadow: inset 0 -1px 0 rgba(0,0,0,0.05);
} 

.menu-toc {
	list-style: none;
}

.menu-toc li a {
	display: block;
	color: #fff;
	font-size: 1.1em;
	line-height: 3.5;
	padding: 0 20px;
	cursor: pointer;
	background: #f1103a;
	border-bottom: 1px solid #dd1338;
}

.menu-toc li a:hover,
.menu-toc li.menu-toc-current a{
	background: #dd1338;
}

Меню навигации будет всегда позиционировано абсолютно, вверху экрана:

.bb-custom-wrapper nav {
	top: 20px;
	left: 60px;
	position: absolute;
	z-index: 1000;
}

Стрелки и кнопка меню также будут абсолютно позиционированы и мы сделаем их круглыми, установив border-radius равный 50%:

.bb-custom-wrapper nav span,
.menu-button {
	position: absolute;
	width: 32px;
	height: 32px;
	top: 0;
	left: 0;
	background: #f1103a;
	border-radius: 50%;
	color: #fff;
	line-height: 30px;
	text-align: center;
	speak: none;
	font-weight: bold;
	cursor: pointer;
}

.bb-custom-wrapper nav span:last-child {
	left: 40px;
}

.bb-custom-wrapper nav span:hover,
.menu-button:hover {
	background: #000;
}

Кнопка меню будет в верхнем левом углу, и еще мы спрячем ее текст:

.menu-button {
	z-index: 1000;
	left: 20px;
	top: 20px;
	text-indent: -9000px;
}

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

.menu-button:after {
	position: absolute;
	content: '';
	width: 50%;
	height: 2px;
	background: #fff;
	top: 50%;
	margin-top: -1px;
	left: 25%;
	box-shadow: 0 -4px #fff, 0 4px #fff;
}

В случае, если JS включен, то там не нужны все эти элементы, поэтому мы просто их спрячем:

.no-js .bb-custom-wrapper nav span,
.no-js .menu-button {
	display: none;
}

Перейдем в внутренним частям BookBlock. Div content должен быть абсолютным и иметь overflow равынм hidden. Это важно, поскольку мы хотим использовать наш собственный скрол только в случаях, когда страница нуждается прокрутке. Если мы не установим overflow в hidden, то контент просто выйдет за границы. И опять же, это имеет значение, только если JS включен, поэтому мы добавим «js» класс:

.js .content {
	position: absolute;
	top: 60px;
	left: 0;
	bottom: 50px;
	width: 100%;
	overflow: hidden;
}

Div scroller будет расти вместе с содержимым, поэтому добавим padding’и здесь:

.scroller {
	padding: 10px 5% 10px 5%;
}

Используем проценты для боковых padding’ов, чтобы макет адаптировался под разные разрешения экрана.

Давайте скроем острые углы, появляющиеся при прокрутке, для чего добавим псевдо-элементы с белым прозрачным градиентом, в верхней и нижней части div’а с контентом:

.js .content:before,
.js .content:after {
	content: '';
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 20px;
	z-index: 100;
	pointer-events: none;
	background: 
		linear-gradient(
			to bottom, 
			rgba(255,255,255,1) 0%, 
			rgba(255,255,255,0) 100%
		);
}

.js .content:after {
	top: auto;
	bottom: 0;
	background: 
		linear-gradient(
			to top, 
			rgba(255,255,255,1) 0%, 
			rgba(255,255,255,0) 100%
		);
}

Это сделает появление текста немного затеменнным.

Добавим стили для текстовых элементов:

.content h2 {
	font-weight: 300;
	font-size: 4em;
	padding: 0 0 10px;
	color: #333;
	margin: 0 1% 40px;
	text-align: left;
	box-shadow: 0 10px 0 rgba(0,0,0,0.02);
	text-shadow: 0 0 2px #fff;
}

.no-js .content h2 {
	padding: 40px 1% 20px;
}

.content p {
	font-size: 1.2em;
	line-height: 1.6;
	font-weight: 300;
	padding: 5px 8%;
	text-align: justify;
}

И наконец, добавим несколько media querie. При отключенном JS, мы не хотим чтобы меню отображалось при ширине экрана менее 800px. Это просто пример того, как мы можем контролировать эти элементы при определенных условиях.

Последний media querie немного уменьшит шрифт, для устройств с маленьким экраном.

@media screen and (max-width: 800px){
	.no-js .menu-panel {
		display: none;
	}

	.no-js .container {
		padding: 0;
	}
}

@media screen and (max-width: 400px){
	.menu-panel,
	.content {
		font-size: 75%;
	}
}

Вот мы и закончили со стилями!

JavaScript

Мы закэшируем некоторые элементы и инициализируем плагин BookBlock. Нам нужно добавить пару вещей при перелистывании страницы, в основном это текущий индекс и поведение jScrollPane. Все это определено в вызове onEndFlip, происходящем в BookBlock.

var $container = $( '#container' ),

	// элемент к которому мы будем применять плагин BookBlock
	$bookBlock = $( '#bb-bookblock' ),

	// страницы BookBlock (bb-item)
	$items = $bookBlock.children(),

	// индекс текущей страницы
	current = 0,

	// инициализация BookBlock
	bb = $( '#bb-bookblock' ).bookblock( {
		speed : 800,
		perspective : 2000,
		shadowSides	: 0.8,
		shadowFlip	: 0.4,
		// после каждого перелистывания...
		onEndFlip : function(old, page, isLimit) {

			// обновляем текущий индекс
			current = page;

			// обновляем выбранную страницу в содержании
			updateTOC();

			// показываем и/или прячем стрелки навигации
			updateNavigation( isLimit );

			// инициализируем jScrollPane для новой страницы в div'е "content"
			setJSP( 'init' );

			// уничтожаем jScrollPane для текущей страницы и div'а
			setJSP( 'destroy', old );

		}
	} ),
	// стрелки навигации
	$navNext = $( '#bb-nav-next' ),
	$navPrev = $( '#bb-nav-prev' ).hide(),

	// содержание
	$menuItems = $container.find( 'ul.menu-toc > li' ),

	// кнопка открытия содержания
	$tblcontents = $( '#tblcontents' ),

	transEndEventNames = {
		'WebkitTransition': 'webkitTransitionEnd',
		'MozTransition': 'transitionend',
		'OTransition': 'oTransitionEnd',
		'msTransition': 'MSTransitionEnd',
		'transition': 'transitionend'
	},

	// имя события transition
	transEndEventName = transEndEventNames[Modernizr.prefixed('transition')],

	// проверяем поддерживается ли transitions
	supportTransitions = Modernizr.csstransitions;

Сначала, свяжем события для некоторых элементов, инициализированных ранее. Также, нам нужно инициализировать jScrollPane для первой (текущей) страницы.

function init() {

	// инициализируем jScrollPane для первой страницы в div'е content
	setJSP( 'init' );
	initEvents();

}

Кроме того, нам потребует инициализировать, реинициализировать, и уничтожать jScrollPane, поэтому определим функцию для этого:

function setJSP( action, idx ) {

	var idx = idx === undefined ? current : idx,
		$content = $items.eq( idx ).children( 'div.content' ),
		apiJSP = $content.data( 'jsp' );

	if( action === 'init' && apiJSP === undefined ) {
		$content.jScrollPane({verticalGutter : 0, hideFocus : true });
	}
	else if( action === 'reinit' && apiJSP !== undefined ) {
		apiJSP.reinitialise();
	}
	else if( action === 'destroy' && apiJSP !== undefined ) {
		apiJSP.destroy();
	}

}

Нам нужно определить следующие события:

  1. Мы будем вызывать методы плагина BookBlock next() и prev() когда будем нажимать на стрелки навигации или когда будем переворачивать страницы.
  2. Содержание будет показываться/скрываться при нажатии на кнопку $tblcontents (меню)
  3. Мы будем вызывать метод ookBlock jump() при выборе страницы в содержании
  4. jScrollPane будет инициализироваться при изменении размера окна

Итак, поехали:

function initEvents() {

	// добавим события навигации
	$navNext.on( 'click', function() {
		bb.next();
		return false;
	} );

	$navPrev.on( 'click', function() {
		bb.prev();
		return false;
	} );

	// добавим события перелистывания
	$items.on( {
		'swipeleft'		: function( event ) {
			if( $container.data( 'opened' ) ) {
				return false;
			}
			bb.next();
			return false;
		},
		'swiperight'	: function( event ) {
			if( $container.data( 'opened' ) ) {
				return false;
			}
			bb.prev();
			return false;
		}
	} );

	// показываем содержание
	$tblcontents.on( 'click', toggleTOC );

	// клик на странице в содержании
	$menuItems.on( 'click', function() {

		var $el = $( this ),
			idx = $el.index(),
			jump = function() {
				bb.jump( idx + 1 );
			};

		current !== idx ? closeTOC( jump ) : closeTOC();

		return false;

	} );

	// реинициализируем jScrollPane при изменении размера окна
	$( window ).on( 'debouncedresize', function() {
		setJSP( 'reinit' );
	} );

}

Видимость стрелок навигации будет зависить от текущей страницы. Если мы на первой странице, то будем видеть только стрелку «вперед», а на последней странице соответственно только «назад».

function updateNavigation( isLastPage ) {

	if( current === 0 ) {
		$navNext.show();
		$navPrev.hide();
	}
	else if( isLastPage ) {
		$navNext.hide();
		$navPrev.show();
	}
	else {
		$navNext.show();
		$navPrev.show();
	}

}

При открытии содержания мы хотим прятать стрелки навигации и показывать их снова при его закрытии.

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

function toggleTOC() {
	var opened = $container.data( 'opened' );
	opened ? closeTOC() : openTOC();
}

function openTOC() {
	$navNext.hide();
	$navPrev.hide();
	$container.addClass( 'slideRight' ).data( 'opened', true );
}

function closeTOC( callback ) {

	$navNext.show();
	$navPrev.show();
	$container.removeClass( 'slideRight' ).data( 'opened', false );
	if( callback ) {
		if( supportTransitions ) {
			$container.on( transEndEventName, function() {
				$( this ).off( transEndEventName );
				callback.call();
			} );
		}
		else {
			callback.call();
		}
	}

}

Пиу! :) Вот и все! Надеюсь Вам понравилось это руководство и когда-нибудь пригодится!

ДемоИсходники

Похожие записи

Немного об авторе: Сергей Белянин

Основной автор этого блога. Еще совсем недавно студент, а сегодня уже Microsoft Certifed Professional, с неукротимым желанием сделать жизнь людей проще, автоматизировав скучную рутинную работу :)