这是一款效果非常酷的jQuery和css3全屏翻页切换页面特效插件。该插件是在BookBlock可预览的css3+js翻书效果的基础上,添加了侧边栏,和前后翻页按钮制作而成的。在插件中我们还集成了 jScrollPane 用来自定义滚动条元素。

下面的jQuery插件将在该插件中被使用:

HTML结构

.container作为wrapper。在里面,使用一个div .menu-panel作为侧边栏。“书本”使用.bb-custom-wrapper包裹起来。

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

JAVASCRIPT

我们需要指定每次翻页之后,当前项的index和jScrollPane 的状态,这些通过onEndFlip 在BookBlock的回调中指定:

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

  // the element we will apply the BookBlock plugin to
  $bookBlock = $( '#bb-bookblock' ),

  // the BookBlock items (bb-item)
  $items = $bookBlock.children(),

  // index of the current item
  current = 0,

  // initialize the BookBlock
  bb = $( '#bb-bookblock' ).bookblock( {
    speed : 800,
    perspective : 2000,
    shadowSides : 0.8,
    shadowFlip  : 0.4,
    // after each flip...
    onEndFlip : function(old, page, isLimit) {
      
      // update the current value
      current = page;

      // update the selected item of the table of contents (TOC)
      updateTOC();

      // show and/or hide the navigation arrows
      updateNavigation( isLimit );

      // initialize the jScrollPane on the content div for the new item
      setJSP( 'init' );

      // destroy jScrollPane on the content div for the old item
      setJSP( 'destroy', old );

    }
  } ),
  // the navigation arrows
  $navNext = $( '#bb-nav-next' ),
  $navPrev = $( '#bb-nav-prev' ).hide(),

  // the table of content items
  $menuItems = $container.find( 'ul.menu-toc > li' ),

  // button to open the TOC
  $tblcontents = $( '#tblcontents' ),

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

  // transition event name
  transEndEventName = transEndEventNames[Modernizr.prefixed('transition')],

  // check if transitions are supported
  supportTransitions = Modernizr.csstransitions;
                

在元素初始化时为它们绑定一些事件,同时我们需要初始化jScrollPane 的当前项。

function init() {
  // initialize jScrollPane on the content div of the first item
  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、当我们点击侧边栏按钮时,侧边栏将被显示/隐藏。
  • 3、当我们点击侧边栏选项时,将调用BookBlock的jump()方法。
  • 4、jScrollPane将在窗口变化时被重置。
function initEvents() {
  // add navigation events
  $navNext.on( 'click', function() {
    bb.next();
    return false;
  } );
  $navPrev.on( 'click', function() {
    bb.prev();
    return false;
  } );
  // add swipe events
  $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;
    }
  } );
  // show TOC
  $tblcontents.on( 'click', toggleTOC );
  // click a menu item
  $menuItems.on( 'click', function() {
    var $el = $( this ),
      idx = $el.index(),
      jump = function() {
        bb.jump( idx + 1 );
      };   
    current !== idx ? closeTOC( jump ) : closeTOC();
    return false;
  } );
  // reinit jScrollPane on window resize
  $( window ).on( 'debouncedresize', function() {
    // reinitialise jScrollPane on the content div
    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来制作侧边栏的动画效果。如果浏览器不支持CSS 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();
    }
  }
}