首页 专题 H5案例 前端导航 UI框架

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

作者:TG 日期: 2016-11-29 字数: 11859 阅读: 6330
关于轮番插件,网上已经有N多种了,比如:swiperSuperSlide等,因此我也不打算完整的开发一个,这里简单的搞一个slide3d,便于介绍轮番的实现原理。

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

可到Github上下载:https://github.com/IronPans/slide3D

1、布局

布局(slide切换效果)需如下

<div class="container3D">   

  <div class="wrapper3D">   

    <div class="slide3D">   

      <img src="image-slider-1.jpg" alt="" />   

    </div>   

    <div class="slide3D">   

      <img src="image-slider-2.jpg" alt="" />   

    </div>   

    <div class="slide3D">   

      <img src="image-slider-3.jpg" alt="" />   

    </div>   

    <div class="slide3D">   

      <img src="image-slider-4.jpg" alt="" />   

    </div>   

    <div class="slide3D">   

      <img src="image-slider-5.jpg" alt="" />   

    </div>   

  </div>   

  <div class="slide3D-pagination"></div>   

  <div class="slide3D-prev-button"></div>   

  <div class="slide3D-next-button"></div>   

</div>

其他切换效果(flip、turn、flat、fragment)布局:

<div class="container3D">   

  <div class="wrapper3D"></div>   

  <div class="slide3D-pagination"></div>   

  <div class="slide3D-prev-button"></div>   

  <div class="slide3D-next-button"></div>   

</div>

图片在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元素中。
你可以打开控制台,可以看到代码是这样的:

<div class="container3D">   

  <div class="wrapper3D">      

    <div class="slide3D">  

      <img src="image-slider-5.jpg" alt="" />      

    </div>   

    /* 这里省略原来的5个slide3D  */

    <div class="slide3D">      

      <img src="image-slider-1.jpg" alt="" />      

    </div>   

  </div> 

  /* 省略分页器和前进后退按钮 */

</div>

当然,这还不行,我们还需要让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);  // 立即执行函数

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


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


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


目录