酷炫的轮番效果是如何实现的

2,455 阅读4分钟
关于轮番插件,网上已经有N多种了,比如:swiperSuperSlide等,因此我也不打算完整的开发一个,这里简单的搞一个slide3d,便于介绍轮番的实现原理。

点击这里看实例:简单轮番例子

可到Github上下载:github.com/IronPans/sl…

1、布局

布局(slide切换效果)需如下
其他切换效果(flip、turn、flat、fragment)布局:
图片在JavaScript脚本中定义。

2、初始化

使用new Slide3D(contianer, params)初始化一个Slide3D,返回初始化后的Slide3D实例。
var mySlide = new Slide3D('.flip', {   
  width: 300,   // 定义宽
  height: 400,  // 定义高
  direction: 'horizontal | vertical',  // 切换方向,只有slide支持
  effect: 'flip',  // 切换效果 flip | turn | slide | flat | fragment   
  sources: ['image-slider-1.jpg', 'image-slider-2.jpg', 'image-slider-3.jpg', 'image-slider-4.jpg', 'image-slider-5.jpg'],   // 除“slide”外,其他切换效果都需要sources指定切换图片
  box:{   // slide效果不需要
    rows: 6,  // 行   
    cols: 3  // 列   
  },   
  pagination: true,  // 是否添加分页器   
  loop: true  // 是否循环,只有slide选择设置,其他切换效果都是循环
});

3、源码大剖析

首先来谈谈slide切换效果,相信大家对slide切换效果都会实现,难点在于循环轮番,如何实现呢?

我是利用wrapper3D这一层来实现滑动的。

其实原理并不难,看下面代码:
if(s.params.loop) {   
  var firstChild = s.wrapper.firstElementChild.cloneNode(true);   
  var lastChild = s.wrapper.lastElementChild.cloneNode(true);   
  s.wrapper.appendChild(firstChild);   
  s.wrapper.insertBefore(lastChild, s.wrapper.firstElementChild);   
  s.setTransitionDuration(s.wrapper, 0);   
  s.currentIndex = 1;   
  s.slideTo(s.activeIndex + 1);   
};
上面代码的意思是,当检测到loop属性为true时,我们就将第一个slide3D和最后一个slide3D的内容复制出来,分别作为最后一个子元素和第一个子元素插入到wrapper3D元素中。
你可以打开控制台,可以看到代码是这样的:
/* 这里省略原来的5个slide3D */
/* 省略分页器和前进后退按钮 */
当然,这还不行,我们还需要让wrapper3D元素滑动一个slide3D元素的宽度,不然你看到的就是最后一张图片了。
s.slideTo(s.activeIndex + 1); 

s.slideTo = function(index) {   
  if(s.params.direction == 'horizontal') {   
    s.setTransform(s.wrapper, 'translate3d(-' + (s.params.width * index) + 'px, 0, 0)');   
  }   
};
加上上面的代码后,你看到的就是第一张图片了。

问题又来了,如何实现第一张和最后一张之间的切换呢?

答案也很简单,我们假设我们有5张轮番图,每一张的宽度是400,那么经过上面的代码处理后,wrapper3D元素的transform是这样的:
transform: translate3d(-400px, 0, 0)
我相信你还记得我们第一张图片其实是最后一张图片,那么从第一张切换到最后一张,只需这样:
transform: translate3d(-400px, 0, 0)  =>  transform: translate3d(0, 0, 0)
当切换完成时,我们需要将transition-duration设为0,然后:
transform: translate3d(-400 * 5px , 0, 0)

由于transition-duration等于0,所以切换是瞬间,会欺骗我们的眼睛,我们是看不到它切换的。


slide切换效果的循环就是这样的,当然,还有更多方法,你可以尝试,接下来讲讲其他的切换效果(flip、turn、flat、fragment),它们之间是大同小异的。


首先要做的是给图片分格:

var r = s.params.box.rows;   
var c = s.params.box.cols;   
var width = Math.ceil(s.params.width / c);  // 定义每格宽度 
var height = Math.ceil(s.params.height / r);   //定义每格高度
var length = total = r * c;   // 总格数
var cssText = '';   
for(var i = 0; i < length; i++) {   
  var left = i % c * width;   
  var top = Math.floor(i / c) * height;   
  var flipItem = document.createElement('div');   
  if(s.params.effect == 'fragment') {   
    cssText += 'opacity:0;';   
  };   
  cssText += 'position:absolute;left:' + left + 'px;top:' + top + 'px;';   
  cssText += 'width:' + width + 'px;height:' + height + 'px;';   
  cssText += 'background-image:url(' + url + ');';   
  cssText += 'background-position:-' + left + 'px -' + top + 'px;';   
  cssText += 'background-size:' + s.params.width + 'px ' + s.params.height + 'px;';   
  cssText += 'background-repeat:no-repeat;';   
  flipItem.style.cssText = cssText;   
  flipItem.change = false;   
  animation[s.params.effect].execute(flipItem);  // 记住这个函数,后面会讲到  
  s.wrapper.appendChild(flipItem);   // 插入
};

估计你看上面的代码会比较乱,原理跟table差不多,相当于一个td对应一个div,自己用代码实现分格试试就知道了。


我们拿fragment切换效果来分析:

定义一个动画函数:

animate: function(item, fn, fnEnd, index) {
  var opacity = 0;   
  var data = [];   
  clearInterval(item.timer);   
  item.timer = setInterval(function() {   // 使用计时器来实现动画
    opacity += 0.05;   // 改变透明度
    data['opacity'] = opacity;   
    data['index'] = index;   
    if(fn) fn.call(item, data);   // 执行fn函数,同时改变fn函数里的this指向和传参 
    if(opacity >= 1) {   
      clearInterval(item.timer);   
      if(fnEnd) fnEnd.call(item);  // 当前item满足条件时执行fnEnd函数,也是改变fnEnd函数里的this指向 
    }   
  }, 20);
}

对每个item(就是每格)执行动画,同时传参。

>animation[s.params.effect].execute(flipItem);

执行的是下面的代码:

(function(item) {   
  setTimeout(function() {   
    animation[s.params.effect].animate(item, function(data) {   
      item.style.opacity = data['opacity'];   // 每次setInterval执行的代码
    }, function() {   
      if(--total == 0) {  // 当所有item完成动画时,执行if代码块里的代码 
       s.wrapper.innerHTML = '';   
       s.lock = false;   
       end();   
       s.wrapper.style.backgroundImage = 'url(' + s.params.sources[s.activeIndex] + ')';   
      }   
    }, i);   
  }, i * 50);   // 你也可以不适应setTimeout,那样所有item动画都会同时执行
})(item);  // 立即执行函数

其他切换效果也是这个原理。


当你理解了原理后,相信你可以开发出更酷炫的效果。


如果有问题,欢迎在下方提问!