/** * bookblock.js v2.0.1 * http://www.codrops.com * * Licensed under the MIT license. * http://www.opensource.org/licenses/mit-license.php * * Copyright 2013, Codrops * http://www.codrops.com */ ;( function( window ) { 'use strict'; // global var document = window.document, Modernizr = window.Modernizr; // https://gist.github.com/edankwan/4389601 Modernizr.addTest('csstransformspreserve3d', function () { var prop = Modernizr.prefixed('transformStyle'); var val = 'preserve-3d'; var computedStyle; if(!prop) return false; prop = prop.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-'); Modernizr.testStyles('#modernizr{' + prop + ':' + val + ';}', function (el, rule) { computedStyle = window.getComputedStyle ? getComputedStyle(el, null).getPropertyValue(prop) : ''; }); return (computedStyle === val); }); function extend( a, b ) { for( var key in b ) { if( b.hasOwnProperty( key ) ) { a[key] = b[key]; } } return a; } function BookBlock( el, options ) { this.el = el; this.options = extend( this.defaults, options ); this._init(); } BookBlock.prototype = { defaults : { // vertical or horizontal flip orientation : 'vertical', // ltr (left to right) or rtl (right to left) direction : 'ltr', // speed for the flip transition in ms speed : 1000, // easing for the flip transition easing : 'ease-in-out', // if set to true, both the flipping page and the sides will have an overlay to simulate shadows shadows : true, // opacity value for the "shadow" on both sides (when the flipping page is over it) // value : 0.1 - 1 shadowSides : 0.2, // opacity value for the "shadow" on the flipping page (while it is flipping) // value : 0.1 - 1 shadowFlip : 0.1, // if we should show the first item after reaching the end circular : false, // if we want to specify a selector that triggers the next() function. example: ´#bb-nav-next´ nextEl : '', // if we want to specify a selector that triggers the prev() function prevEl : '', // autoplay. If true it overwrites the circular option to true autoplay : false, // time (ms) between page switch, if autoplay is true interval : 3000, // callback after the flip transition // old is the index of the previous item // page is the current item´s index // isLimit is true if the current page is the last one (or the first one) onEndFlip : function(old, page, isLimit) { return false; }, // callback before the flip transition // page is the current item´s index onBeforeFlip : function(page) { return false; } }, _init : function() { // orientation class this.el.className += ' bb-' + this.options.orientation; // items this.items = Array.prototype.slice.call( this.el.querySelectorAll( '.bb-item' ) ); // total items this.itemsCount = this.items.length; // current item´s index this.currentIdx = 0; // previous item´s index this.previous = -1; // show first item this.current = this.items[ this.currentIdx ]; this.current.style.display = 'block'; // get width of this.el // this will be necessary to create the flipping layout this.elWidth = this.el.offsetWidth; var transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'msTransition': 'MSTransitionEnd', 'transition': 'transitionend' }; this.transEndEventName = transEndEventNames[Modernizr.prefixed( 'transition' )]; // support css 3d transforms && css transitions && Modernizr.csstransformspreserve3d this.support = Modernizr.csstransitions && Modernizr.csstransforms3d && Modernizr.csstransformspreserve3d; // initialize/bind some events this._initEvents(); // start slideshow if ( this.options.autoplay ) { this.options.circular = true; this._startSlideshow(); } }, _initEvents : function() { var self = this; if ( this.options.nextEl !== '' ) { document.querySelector( this.options.nextEl ).addEventListener( 'click', function() { self._action( 'next' ); return false; } ); document.querySelector( this.options.nextEl ).addEventListener( 'touchstart', function() { self._action( 'next' ); return false; } ); } if ( this.options.prevEl !== '' ) { document.querySelector( this.options.prevEl ).addEventListener( 'click', function() { self._action( 'prev' ); return false; } ); document.querySelector( this.options.prevEl ).addEventListener( 'touchstart', function() { self._action( 'prev' ); return false; } ); } window.addEventListener( 'resize', function() { self._resizeHandler(); } ); }, _action : function( dir, page ) { this._stopSlideshow(); this._navigate( dir, page ); }, _navigate : function( dir, page ) { if ( this.isAnimating ) { return false; } // callback trigger this.options.onBeforeFlip( this.currentIdx ); this.isAnimating = true; // update current value this.current = this.items[ this.currentIdx ]; if ( page !== undefined ) { this.currentIdx = page; } else if ( dir === 'next' && this.options.direction === 'ltr' || dir === 'prev' && this.options.direction === 'rtl' ) { if ( !this.options.circular && this.currentIdx === this.itemsCount - 1 ) { this.end = true; } else { this.previous = this.currentIdx; this.currentIdx = this.currentIdx < this.itemsCount - 1 ? this.currentIdx + 1 : 0; } } else if ( dir === 'prev' && this.options.direction === 'ltr' || dir === 'next' && this.options.direction === 'rtl' ) { if ( !this.options.circular && this.currentIdx === 0 ) { this.end = true; } else { this.previous = this.currentIdx; this.currentIdx = this.currentIdx > 0 ? this.currentIdx - 1 : this.itemsCount - 1; } } this.nextItem = !this.options.circular && this.end ? this.current : this.items[ this.currentIdx ]; this.items.forEach( function( el, i ) { el.style.display = 'none'; } ); if ( !this.support ) { this._layoutNoSupport( dir ); } else { this._layout( dir ); } }, _layoutNoSupport : function(dir) { this.nextItem.style.display = 'block'; this.end = false; this.isAnimating = false; var isLimit = dir === 'next' && this.currentIdx === this.itemsCount - 1 || dir === 'prev' && this.currentIdx === 0; // callback trigger this.options.onEndFlip( this.previous, this.currentIdx, isLimit ); }, // creates the necessary layout for the 3d structure and triggers the transitions _layout : function(dir) { var self = this, // basic structure: 1 element for the left side. s_left = this._addSide( 'left', dir ), // 1 element for the flipping/middle page s_middle = this._addSide( 'middle', dir ), // 1 element for the right side s_right = this._addSide( 'right', dir ), // overlays o_left = s_left.querySelector( 'div.bb-overlay' ), o_middle_f = s_middle.querySelector( 'div.bb-front' ).querySelector( 'div.bb-flipoverlay' ), o_middle_b = s_middle.querySelector( 'div.bb-back' ).querySelector( 'div.bb-flipoverlay' ), o_right = s_right.querySelector( 'div.bb-overlay' ), speed = this.end ? 400 : this.options.speed; var fChild = this.items[0]; this.el.insertBefore( s_left, fChild ); this.el.insertBefore( s_middle, fChild ); this.el.insertBefore( s_right, fChild ); s_left.style.zIndex = 102; s_middle.style.zIndex = 103; s_right.style.zIndex = 101; s_middle.style.transitionDuration = speed + 'ms'; s_middle.style.transitionTimingFunction = this.options.easing; s_middle.addEventListener( this.transEndEventName, function( event ) { if ( (" " + event.target.className + " ").replace(/[\n\t]/g, " ").indexOf(" bb-page ") > -1 ) { Array.prototype.slice.call( self.el.querySelectorAll( '.bb-page' ) ).forEach( function( el, i ) { self.el.removeChild( el ); } ); self.nextItem.style.display = 'block'; self.end = false; self.isAnimating = false; var isLimit = dir === 'next' && self.currentIdx === self.itemsCount - 1 || dir === 'prev' && self.currentIdx === 0; // callback trigger self.options.onEndFlip( self.previous, self.currentIdx, isLimit ); } } ); if ( dir === 'prev' ) { s_middle.className += ' bb-flip-initial'; } // overlays if ( this.options.shadows && !this.end ) { if( dir === 'next' ) { o_middle_f.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear'; o_middle_b.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms'; o_middle_b.style.opacity = this.options.shadowFlip; o_left.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms'; o_right.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear'; o_right.style.opacity = this.options.shadowSides; } else if( dir === 'prev' ) { o_middle_f.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms'; o_middle_f.style.opacity = this.options.shadowFlip; o_middle_b.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear'; o_left.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear'; o_left.style.opacity = this.options.shadowSides; o_right.style.transition = 'opacity ' + this.options.speed / 2 + 'ms ' + 'linear' + ' ' + this.options.speed / 2 + 'ms'; } } setTimeout( function() { // first && last pages lift slightly up when we can't go further s_middle.className += self.end ? ' bb-flip-' + dir + '-end' : ' bb-flip-' + dir; // overlays if ( self.options.shadows && !self.end ) { o_middle_f.style.opacity = dir === 'next' ? self.options.shadowFlip : 0; o_middle_b.style.opacity = dir === 'next' ? 0 : self.options.shadowFlip; o_left.style.opacity = dir === 'next' ? self.options.shadowSides : 0; o_right.style.opacity = dir === 'next' ? 0 : self.options.shadowSides; } }, 25 ); }, // adds the necessary sides (bb-page) to the layout _addSide : function( side, dir ) { var sideEl = document.createElement( 'div' ); sideEl.className = 'bb-page'; switch (side) { case 'left': /*