这是一款效果非常炫酷的jQueryCSS3 3D折纸面板展开页面特效插件。当用户点击某个导航按钮时,对应内容的面板会像折叠的纸张一样展开,关闭面板时,左右面板又会像纸张一样折叠回去。

纸张折叠效果是当前非常流行的一种网页特效,特别是在一些移动手机的APP中,例如iOS Peek Calendar app。现在,通过 CSS3 transformation 和 transitions,我们可以在浏览器中制作出相同的折纸特效。

制作方法

HTML结构

该折纸面板特效的HTML结构分为两个主要部分:一个无序列表,包含.cd-item子元素,并且无序列表被包裹在一个<main>元素中。另外一个是.cd-folding-panel元素,它包含折纸面板的内容.cd-fold-content和两个纸张面板(.left-fold.right-fold

<main class="cd-main">
  <ul class="cd-gallery">
    <li class="cd-item">
      <a href="item-1.html">
        <div>
          <h2>Title 1</h2>
          <p>Lorem ipsum dolor sit amet, consectetur.</p>
          <b>View More</b>
        </div>
      </a>
    </li>
 
    <li class="cd-item">
      <!-- content here -->
    </li>
 
    <!-- additional list items here -->
  </ul> <!-- .cd-gallery -->
</main> <!-- .cd-main -->
 
<div class="cd-folding-panel">
  
  <div class="fold-left"></div> <!-- this is the left fold -->
  
  <div class="fold-right"></div> <!-- this is the right fold -->
  
  <div class="cd-fold-content">
    <!-- content will be loaded using javascript -->
  </div>
 
  <a class="cd-close" href="#0"></a>
</div> <!-- .cd-folding-panel -->                
              
CSS样式

为了制作折纸动画效果,插件在.left-fold.right-fold.cd-main.cd-item元素上使用了CSS3 Transformations

左右两面纸张的动画效果是在.left-fold.right-fold元素的::after伪元素制作动画。

在移动手机上,只有右边的面板会发生动画效果(左边的纸张面板.left-fold会使用display: none来隐藏)。默认情况下,.cd-folding-panel元素和它的子元素.right-panel是固定定位(fixed)的,并且覆盖整个屏幕(但是它的可见性被设置为hidden)。当用户点击.cd-item的时候,.cd-main的内容使用.fold-is-open class移动到右边,.right-fold::after被移动到屏幕的中间,并进行一定角度的旋转(在.cd-folding-panel元素上使用.is-open)。

.cd-main {
  overflow-x: hidden;
}
.cd-main > * {
  transition: transform 0.5s 0.4s;
}
.cd-main.fold-is-open > * {
  /* on mobile - translate .cd-main content to the right when the .cd-folding-panel is open */
  transform: translateX(100%);
  transition: transform 0.5s 0s;
}
 
.cd-folding-panel {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  visibility: hidden;
  overflow: hidden;
  transition: visibility 0s 0.9s;
}
.cd-folding-panel .fold-left,
.cd-folding-panel .fold-right {
  /* the :after elements of .fold-left and .fold-right are the 2 fold sides */
  width: 100%;
  height: 100vh;
  overflow: hidden;
  /* enable a 3D-space for children elements */
  perspective: 2000px;
}
.cd-folding-panel .fold-right {
  perspective-origin: 0% 50%;
}
.cd-folding-panel .fold-left {
  /* on mobile only the right fold side is visible */
  display: none;
}
.cd-folding-panel .fold-right::after {
  /* 2 fold sides */
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  transform-origin: right center;
  transform: translateX(-100%) rotateY(-90deg);
  transition: transform 0.5s 0.4s, background-color 0.5s 0.4s;
}
.cd-folding-panel.is-open {
  visibility: visible;
  transition: visibility 0s 0s;
}
.cd-folding-panel.is-open .fold-right::after {
  transform: translateX(0);
  transition: transform 0.5s 0s, background-color 0.5s 0s;
}                
              

在桌面设备上(屏幕大于1100像素),左右纸张面板的::after伪元素都被执行动画。.cd-folding-panel被放置到屏幕的中间(宽度被设置为800像素)。.left-panel.right-panel被设置为左浮动float: left,并且它们的宽度都是400像素。它们的::after伪元素都被旋转90度rotateY(-90deg),左面板向左移动translateX(100%),右面板向右移动translateX(-100%)

当用户点击.cd-item的时候,它们会向左:nth-of-type(2n+1)或向右:nth-of-type(2n)进行移动。并且它们的::after伪元素也被移动和旋转。

@media only screen and (min-width: 1100px) {
  .cd-item {
    width: 50%;
    float: left;
    transition: transform 0.5s 0.4s;
  }
  .fold-is-open .cd-item {
    transition: transform 0.5s 0s;
    transform: translateX(-400px);
  }
  .fold-is-open .cd-item:nth-of-type(2n) {
    transform: translateX(400px);
  }
}
 
@media only screen and (min-width: 1100px) {
  .cd-folding-panel {
    left: 50%;
    transform: translateX(-50%);
    width: 800px;
  }
  .cd-folding-panel .fold-left,
  .cd-folding-panel .fold-right {
    width: 50%;
    float: left;
    height: 100%;
  }
  .cd-folding-panel .fold-right {
    /* change perspective-origin so that the 2 fold sides have the same vanishing point */
    perspective-origin: 0% 50%;
  }
  .cd-folding-panel .fold-right::after {
    transform-origin: right center;
    transform: translateX(-100%) rotateY(-90deg);
  }
  .cd-folding-panel .fold-left {
    display: block;
    /* change perspective-origin so that the 2 fold sides have the same vanishing point */
    perspective-origin: 100% 50%;
  }
  .cd-folding-panel .fold-left::after {
    transform-origin: left center;
    transform: translateX(100%) rotateY(90deg);
  }
  .cd-folding-panel.is-open .fold-right::after,
  .cd-folding-panel.is-open .fold-left::after {
    transform: translateX(0);
    transition: transform 0.5s 0s, background-color 0.5s 0s;
  }
}                
              

需要注意的是,插件中修改了左右纸张面板的透视原点perspective-origin。因为每一个::after伪元素都有它自己的3d空间消失点,消失点是它的父元素的中心,在这个效果中,分别是.left-panel.right-panel的中心。插件中将左右纸张的透视原点进行了修改,使它们的消失点都位于视口的中心位置。

.cd-folding-panel .fold-right {
  perspective-origin: 0% 50%;
}
.cd-folding-panel .fold-left {
  perspective-origin: 100% 50%;
}                
              
JAVASCRIPT

在index.html文件中,.cd-fold-content元素初始化时设置为空元素。

当用户点击了一个.cd-item按钮,插件是load()方法在.cd-fold-content元素中插入匹配的内容。

当新的内容被插入之后,插件回味各个元素添加相应的class,之后折纸动画特效被触发。

/* open folding content */
$('.cd-gallery a').on('click', function(event){
  event.preventDefault();
  openItemInfo($(this).attr('href'));
});
function openItemInfo(url) {
  /* check if mobile or desktop */
  var mq = viewportSize();
  if( $('.cd-gallery').offset().top > $(window).scrollTop() && mq != 'mobile') {
    /* if content is visible above the .cd-gallery - scroll before opening the folding panel */
    $('body,html').animate({
      'scrollTop': $('.cd-gallery').offset().top
    }, 100, function(){ 
            toggleContent(url, true);
        }); 
 
  } else {
    toggleContent(url, true);
  }
}
 
function toggleContent(url, bool) {
  if( bool ) {
    /* load and show new content */
    $('.cd-fold-content').load(url+' .cd-fold-content > *', function(event){
      $('body').addClass('overflow-hidden');
      $('.cd-folding-panel').addClass('is-open');
      $('.cd-main').addClass('fold-is-open');
    });
 
  } else {
    /* close the folding panel */
    $('.cd-folding-panel').removeClass('is-open')
    $('.cd-main').removeClass('fold-is-open');
    
    /* ...*/
  }
  
}                
              

插件中只是提供了一个基本的load()方法来加载内容。你可以自行修改,使用$.ajax来加载内容,或进行一些其他的处理。