Wavescroll是一款基于GSAP的响应式全屏背景图片jQuery幻灯片插件。该幻灯片插件支持鼠标拖动、鼠标滚动和移动触摸事件,可以制作出炫酷的波浪滚动切换效果。

该幻灯片插件主要使用的技术有:jQuery, CSS3, requestAnimationFrame 和 GSAP的TweenMax.js

使用方法

HTML结构

该jQuery幻灯片的HTML结构如下:

<div class="ws-pages">
  <div class="ws-bgs">
    <div class="ws-bg"></div>
    <div class="ws-bg"></div>
    <div class="ws-bg"></div>
    <div class="ws-bg"></div>
    <div class="ws-bg"></div>
  </div>
  <div class="ws-text">
    <h2 class="ws-text__heading">
      <span>This is Slide 1</span>
    </h2>
    <h2 class="ws-text__heading">
      <span>This is Slide 2</span>
    </h2>
    <h2 class="ws-text__heading">
      <span>This is Slide 3</span>
    </h2>
    <h2 class="ws-text__heading">
      <span>This is Slide 4</span>
    </h2>
    <h2 class="ws-text__heading">
      <span>This is Slide 5</span>
      <span>The end</span>
    </h2>
  </div>
</div>
                
CSS样式

该幻灯片的主要CSS样式如下:所有幻灯片都通过background-size: cover;来设置为全屏模式。.ws-bg__part是制作百叶窗效果的每一个窗叶。每个幻灯片slide分为24个窗叶,分别使用使用ws-bg__part-n:after伪元素来制作。

.ws-pages {
  overflow: hidden;
  position: relative;
  height: 100%;
}

.ws-bgs {
  position: relative;
  height: 100%;
}

.ws-bg {
  height: 100%;
  background-size: cover;
  background-position: center center;
}

.ws-pages.s--ready .ws-bg { background: none !important; }

.ws-bg:after {
  content: "";
  display: table;
  clear: both;
}

.ws-bg__part {
  overflow: hidden;
  position: relative;
  float: left;
  width: 4.16667%;
  height: 100%;
  background-size: cover;
  background-position: center center;
  cursor: -webkit-grab;
  cursor: grab;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.ws-bg__part:after {
  content: "";
  position: absolute;
  top: 0;
  width: 100vw;
  height: 100%;
  background: inherit;
}

.ws-bg__part-1:after { left: 0vw; }

.ws-bg__part-2:after { left: -4.16667vw; }

.ws-bg__part-3:after { left: -8.33333vw; }

.ws-bg__part-4:after { left: -12.5vw; }

.ws-bg__part-5:after { left: -16.66667vw; }

.ws-bg__part-6:after { left: -20.83333vw; }

.ws-bg__part-7:after { left: -25vw; }

.ws-bg__part-8:after { left: -29.16667vw; }

.ws-bg__part-9:after { left: -33.33333vw; }

.ws-bg__part-10:after { left: -37.5vw; }

.ws-bg__part-11:after { left: -41.66667vw; }

.ws-bg__part-12:after { left: -45.83333vw; }

.ws-bg__part-13:after { left: -50vw; }

.ws-bg__part-14:after { left: -54.16667vw; }

.ws-bg__part-15:after { left: -58.33333vw; }

.ws-bg__part-16:after { left: -62.5vw; }

.ws-bg__part-17:after { left: -66.66667vw; }

.ws-bg__part-18:after { left: -70.83333vw; }

.ws-bg__part-19:after { left: -75vw; }

.ws-bg__part-20:after { left: -79.16667vw; }

.ws-bg__part-21:after { left: -83.33333vw; }

.ws-bg__part-22:after { left: -87.5vw; }

.ws-bg__part-23:after { left: -91.66667vw; }

.ws-bg__part-24:after { left: -95.83333vw; }                  
                

幻灯片的图片使用背景图片来制作。

.ws-bg:nth-child(1) { background-image: url(1.jpg); }

.ws-bg:nth-child(1) .ws-bg__part { 1.jpg); }

.ws-bg:nth-child(2) { background-image: url(2.jpg); }

.ws-bg:nth-child(2) .ws-bg__part { background-image: url(2.jpg); }

.ws-bg:nth-child(3) { background-image: url(3.jpg); }

.ws-bg:nth-child(3) .ws-bg__part { background-image: url(3.jpg); }

.ws-bg:nth-child(4) { background-image: url(4.jpg); }

.ws-bg:nth-child(4) .ws-bg__part { background-image: url(4.jpg); }

.ws-bg:nth-child(5) { background-image: url(5.jpg); }

.ws-bg:nth-child(5) .ws-bg__part { background-image: url(5.jpg); }                  
                
初始化插件

Wavescroll是基于GSAP来制作的,所以在使用时要在页面中引入jQuery和TweenMax.js文件。

<script src="/path/to/jquery.min.js"></script> 
<script src="/path/to/TweenMax.min.js"></script>                  
                

Wavescroll幻灯片的核心js代码如下:

window.requestAnimFrame = (function() {
  return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    function(callback){
    window.setTimeout(callback, 1000 / 60);
  };
})();

function rafThrottle(fn) {
  var busy = false;
  return function() {
    if (busy) return;
    busy = true;
    fn.apply(this, arguments);
    requestAnimFrame(function() {
      busy = false;
    });
  };
};

$(document).ready(function() {

  var $wsPages = $(".ws-pages");
  var $headings = $(".ws-text__heading");
  var bgParts = 24;
  var staggerVal = 65;
  var staggerStep = 4;
  var textH = $(".ws-text").height();
  var winW = $(window).width();
  var winH = $(window).height();
  var curPage = 1;
  var numOfPages = $(".ws-bg").length;
  var changeAT = 0.5;
  var waveStartDelay = 0.2;
  var waveStagger = 0.013;
  var waveBlocked = false;
  var index = 1;
  var startY = 0;
  var deltaY = 0;
  var headingsY = 0;
  var $parts;
  
  function initBgs() {
    var arr = [];
    for (var i = 1; i <= bgParts; i++) {
      var $part = $('<div class="ws-bg__part">');
      $part.addClass("ws-bg__part-" + i);
      arr.push($part);
    }
    $(".ws-bg").append(arr);
    $wsPages.addClass("s--ready");
    $parts = $(".ws-bg__part");
    changePages();
  };

  initBgs();

  function changePages() {
    var y = (curPage - 1) * winH * -1;
    var textY = textH * (curPage - 1) * -1;
    var leftMax = index - 1;
    var rightMin = index + 1;

    TweenMax.to($(".ws-bg__part-" + index), changeAT, {y: y});

    for (var i = leftMax; i > 0; i--) {
      var d = (index - i) * waveStagger;
      TweenMax.to($(".ws-bg__part-" + i), changeAT - d, {y: y, delay: d});
    }

    for (var j = rightMin; j <= bgParts; j++) {
      var d = (j - index) * waveStagger;
      TweenMax.to($(".ws-bg__part-" + j), changeAT - d, {y: y, delay: d});
    }
    
    TweenMax.to($headings, changeAT, {y: textY});
  };

  function waveChange() {
    waveBlocked = true;
    var y = (curPage - 1) * winH * -1;
    var textY = textH * (curPage - 1) * -1;
    for (var i = 1; i <= bgParts; i++) {
      var $part = $(".ws-bg__part-" + i);
      var d = (i - 1) * waveStagger + waveStartDelay;
      TweenMax.to($part, changeAT, {y: y, delay: d});
    }
    
    TweenMax.to($headings, changeAT, {y: textY, delay: d});

    var delay = (changeAT + waveStagger * (bgParts - 1)) * 1000;
    setTimeout(function() {
      waveBlocked = false;
    }, delay);
  };

  function navigateUp() {
    if (curPage > 1) curPage--;
  };

  function navigateDown() {
    if (curPage < numOfPages) curPage++;
  };

  function navigateWaveUp() {
    if (curPage === 1) return;
    curPage--;
    waveChange();
  };

  function navigateWaveDown() {
    if (curPage === numOfPages) return;
    curPage++;
    waveChange();
  };

  function movePart($part, y) {
    var y = y - (curPage - 1) * winH;
    var headY = headingsY - (curPage - 1) * textH;
    TweenMax.to($part, changeAT, {y: y, ease: Back.easeOut.config(4)});
    TweenMax.to($headings, changeAT, {y: headY});
  };

  function moveParts(y, index) {
    var leftMax = index - 1;
    var rightMin = index + 1;
    var stagLeft = 0;
    var stagRight = 0;
    var stagStepL = -staggerStep;
    var stagStepR = -staggerStep;
    var sign = (y > 0) ? -1 : 1;

    movePart($(".ws-bg__part-" + index), y);

    for (var i = leftMax; i > 0; i--) {
      var step = index - i;
      stagStepL += (step <= 15) ? staggerStep : 1;
      var sVal = staggerVal - stagStepL;
      if (sVal < 0) sVal = 0;
      stagLeft += sVal;
      var nextY = y + stagLeft * sign;
      if (Math.abs(y) < Math.abs(stagLeft)) nextY = 0;
      movePart($(".ws-bg__part-" + i), nextY);
    }

    for (var j = rightMin; j <= bgParts; j++) {
      var step = j - index;
      stagStepR += (step <= 15) ? staggerStep : 1;
      var sVal = staggerVal - stagStepR;
      if (sVal < 0) sVal = 0;
      stagRight += sVal;
      var nextY = y + stagRight * sign;
      if (Math.abs(y) < Math.abs(stagRight)) nextY = 0;
      movePart($(".ws-bg__part-" + j), nextY);
    }
  };

  var mousemoveHandler = rafThrottle(function(e) {
    var y = e.pageY;
    var x = e.pageX;
    index = Math.ceil(x / winW * bgParts);

    deltaY = y - startY;
    headingsY = textH * deltaY / winH;
    moveParts(deltaY, index);
  });
  
  var touchmoveHandler = rafThrottle(function(e) {
    e.preventDefault();
    var y = e.originalEvent.touches[0].pageY;
    var x = e.originalEvent.touches[0].pageX;
    index = Math.ceil(x / winW * bgParts);

    deltaY = y - startY;
    headingsY = textH * deltaY / winH;
    moveParts(deltaY, index);
  });

  var swipeEndHandler = function() {
    $(document).off("mousemove", mousemoveHandler);
    $(document).off("touchmove", touchmoveHandler);
    $(document).off("mouseup touchend", swipeEndHandler);

    if (!deltaY) return;

    if (deltaY / winH >= 0.5) navigateUp();
    if (deltaY / winH <= -0.5) navigateDown();
    changePages();
  };

  $(document).on("mousedown touchstart", ".ws-bg__part", function(e) {
    startY = e.pageY || e.originalEvent.touches[0].pageY;
    deltaY = 0;

    $(document).on("mousemove", mousemoveHandler);
    $(document).on("touchmove", touchmoveHandler);

    $(document).on("mouseup touchend", swipeEndHandler);
  });

  $(document).on("mousewheel DOMMouseScroll", function(e) {
    if (waveBlocked) return;
    if (e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0) {
      navigateWaveUp();
    } else { 
      navigateWaveDown();
    }
  });

  $(document).on("keydown", function(e) {
    if (waveBlocked) return;
    if (e.which === 38) {
      navigateWaveUp();
    } else if (e.which === 40) {
      navigateWaveDown();
    }
  });

  $(window).on("resize", function() {
    winW = $(window).width();
    winH = $(window).height();
    changePages();
  });
});