原生JavaScript无缝轮播图特效

96
酷酷de柯基
2017.03.30 23:28* 字数 1022

什么是轮播图?

轮播图,图片旋转器,滑片,无论你怎么叫这玩意,它在网络上无处不在。轮播图在电商网站主页上广泛应用,大多数电商网站的主页上都有它:

高大上的无缝轮播

本轮播图Demo

由于仿色压缩将就看吧!(手动滑稽)

喏~两个链接甩给泥萌
Github仓库文件
在线Demo(移动端加载很慢)

前言

网上有很多的轮播图特效,部分采用的transition属性结合JS实现滚动,但是很遗憾没有无缝效果,什么是无缝?我给前端小白画了一个示意图。另外本文是我写的第一篇文章,难免有生疏之处。包含很多知识点回顾,很适合JS新手学习。

实际上1-5才是真正的图片(红色的东西是个iphone哈哈,当作是container吧)

当焦点位于图片1时,如果再往前滚的话,整个队列会被拉倒真正的第五张图。至于真正的图片1前面的副本图片5只是让用户产生视觉差,从而不会让用户明显感觉到图片被倒向了图片5,看起来就像时无缝轮播。
同理当焦点位于图片5时,如果再往后滚,道理同上不再赘述。

实现功能

1.当鼠标放入容器内时左右出现控制按钮,并且轮播动画停止。
2.当鼠标移出时,控制按钮隐藏,轮播继续。
3.焦点随图片的滚动而变化。
4.跳跃点击焦点,会跳转到相应的图片。
5.以及前沿所述的无缝轮播。

开搞~~

Html代码:

(内有注释解析)

<div id="container">
    <div id="list" style="left: -600px;">//初始状态是真正的图片1,也就是绝对定位-600px
        ![](img/555.jpg)
        ![](img/111.jpg)
        ![](img/222.jpg)
        ![](img/333.jpg)
        ![](img/444.jpg)
        ![](img/555.jpg)
        ![](img/111.jpg)
    </div>
    <div id="buttons">
        <span index="1" class="on"></span>//动画开始时小圆点位于第一个
        <span index="2"></span>
        <span index="3"></span>
        <span index="4"></span>
        <span index="5"></span>
    </div>
    <a  id="prev" class="arrow"><</a>//前一个箭头
    <a  id="next" class="arrow">></a>//后一个箭头
</div>

开始讲解代码:

1.箭头控制

var prev=document.getElementById("prev");
var next=document.getElementById("next");
var list=document.getElementById("list");
prev.onclick=function(){
                 list.style.left=parseInt(list.style.left)-600+"px";
                }
next.onclick=function(){
                 list.style.left=parseInt(list.style.left)+600+"px";
                }

给a标签绑定点击事件,先获取listleft属性并且取出数字进行运算操作。下面我们来做一些优化,把定义一个function animate()函数,传入offset参数,并把var newLeft=parseInt(list.style.left) +offset;,然后让prevnext的点击事件调用这个函数。

function animate(offset){
                 list.style.left=newLeft+offset+"px";
            }
            prev.onclick=function(){
                animate(-600);
            }
            next.onclick=function(){
                animate(600);
            }

然后我们会发现当我们点击到第五张图片left=-3000px时,它应该滚到真正的第1张而不是副本1。我们可以像下面这样写实现无缝滚动:

function animate(offset){
 list.style.left=newLeft+offset+"px";       
if (newLeft >-600) {
 list.style.left=-3000+"px";
    };
 if (newLeft <-3000) {
list.style.left=-600+"px";
    };
 }
用箭头控制图片的轮播大工告成!!(撒花~)

2.焦点跟随

这个功能可能比较难以理解,其实和第一部分是一样的。首先考虑到prev和next点击事件中要有标记,这个标记就是一个挂钩,再定义一个函数利用这个挂钩来跟随焦点。我们用showButton()函数,全局定义变量var index=1先让第一幅图的焦点亮起来。下面我们来看代码(忽略animate变量,下一部分我们会讲到):

var buttons=document.getElementById("buttons").getElementsByTagName("span");
var index=1//buttons[0].classNme="on";
 var animated=false;//全局动画停止标记
function shownButton(){
 /* for循环的作用就是当prev和next每click一次,
 就会清除一次前一个class为on的span元素*/
                 for (var i = 0; i < buttons.length ; i++) {
                     buttons[i].className="";      
                }
           buttons[index -1].className="on";//焦点跟随
            } 
prev.onclick=function(){
                if (!animated) {//这里判断
                if (index==1) {
/*这就是我们下面要讲到的当焦点位于第一个span时,让它跳转到第5个*/
                    index=5;
                }else {
                   index -=1; 
                }
                
                shownButton();//把上一个亮起的灭掉,下一个亮起
                     animate(600);
                }
            };
            next.onclick=function(){   
                if (!animated) {/*全局变量animated=false,那么!animated=true
                     if (index==5) {
                    index=1;
                }else {
                   index +=1; 
                }    
                     shownButton();
                     animate(-600);
                }
            };

3.焦点轮播(实现功能第四点)

当图片位于第一个焦点时,我点击第四的焦点。left=-600px变成了left=-2400px话不多说,来看下面的代码吧。

 for (var i = 0; i < buttons.length; i++) {
                buttons[i].onclick=function(){
                    //无关紧要,性能优化使用,目的是当index和myindex相等时,退出函数
                    if (this.className=="on") {
                        return;
                    }
                    var myIndex=parseInt(this.getAttribute("index"));//index不是a的固有属性,因此要用dom调用
                    var offset=-600*(myIndex-index);
                     if (!animated) {/*又出现了这个animated,
在此意思是如果animate()执行过程中,animated=true,那么animate()不能再次被 调用*/
                     animate(offset);
                }
                    index=myIndex;//更新index,便于下一次的轮播
                     shownButton();               
                }
            }           

4.动画函数

动画函数go()整合到了animate()函数里了。只可意会不可言传(手动滑稽)直接上代码吧:

function animate(offset){ 
             
                var speed = offset/30;//每次的位移,分母越大越流畅
                  animated=true;//动画正在运行,别的事件不要来打扰
                 var newLeft=parseInt(list.style.left) +offset;
                function go(){
                    if ( (speed > 0 && parseInt(list.style.left) < newLeft) || (speed < 0 && parseInt(list.style.left) > newLeft)) {
                        list.style.left = parseInt(list.style.left) + speed + 'px';
                        setTimeout(go, inteval);//递归函数
                    }
                    else 
                    {       
                       animated=false;
                     list.style.left=newLeft+"px";
                         if (newLeft >-600) {
                     list.style.left=-3000+"px";
                         };
                        if (newLeft <-3000) {
                     list.style.left=-600+"px";
                      };
                    }
                }
                go();
            };     

5.定时器

HTML DOM setInterval() 方法

var container=document.getElementById("container");
var timer;
function play(){
                timer=setInterval(function(){
                    next.onclick();
                },2000);
            }
            function stop(){
                clearInterval(timer);
            }
           play();
        container.onmouseover=stop;
        container.onmouseout=play;

最后放一遍总的JS代码

高大上的JS代码(滑稽)
< script > 
window.onload = function () 
{
    var prev = document.getElementById("prev");
    var next = document.getElementById("next");
    var list = document.getElementById("list");
    var buttons = document.getElementById("buttons").getElementsByTagName("span");
    var container = document.getElementById("container");
    var index = 1;
    var timer;
    var animated = false;
    function shownButton()
    {
        for (var i = 0; i < buttons.length ; i++) 
        {
            if ( buttons[i].className == 'on')
            {
                buttons[i].className = '';
                /* prev和next每click一次,
                        就会清除一次前一个class为on的span元素,
                        所以没有必要再去循环下面的span*/
                break;
            }
            //  或者直接遍历清除  buttons[i].className="";
        }
        buttons[index - 1].className = "on";
    }
    function animate(offset)
    {
        var time = 300;
        var inteval = 10;
        var speed = offset / (time / inteval);
        animated = true;//更改全局变量,防止跳图
        var newLeft = parseInt(list.style.left) + offset;
        function go()
        {
            if ( (speed > 0 && parseInt(list.style.left) < newLeft) || (speed < 0 && parseInt(list.style.left) > newLeft)) {
                list.style.left = parseInt(list.style.left) + speed + 'px';
                setTimeout(go, inteval);
            }
            else 
            {
                animated = false;//全局变量
                 list.style.left=newLeft+"px";
                if (newLeft > -600) {
                    list.style.left =- 3000 + "px";
                };
                if (newLeft <- 3000) {
                    list.style.left =- 600 + "px";
                };
            }
        }
        go();
    };
    prev.onclick = function ()
    {
        /*添加一个if判断index为1时,如果继续往前滚的话就让index返回第五个span
                但是当快速点击arrow时会出现一种span点亮延迟的情况。可以尝试把判断index是否大于1或小于5的情况放进
                判断是否animated的if语句中,先判断能不能点击,再点亮。
                */
        if (!animated) {
            if (index == 1) {
                index = 5;
            }
            else {
                index -= 1;
            }
            shownButton();
            animate(600);
        }
    };
    next.onclick = function ()
    {
        if (!animated) {
            if (index == 5) {
                index = 1;
            }
            else {
                index += 1;
            }
            shownButton();
            animate(-600);
        }
    };
    for (var i = 0; i < buttons.length; i++) 
    {
        buttons[i].onclick = function ()
        {
            //无关紧要
            if (this.className == "on") {
                return;
            }
            var myIndex = parseInt(this.getAttribute("index"));
            var offset =- 600 * (myIndex - index);
            if (!animated) {
                animate(offset);
            }
            index = myIndex;
            shownButton();
        }
    }
    function play()
    {
        timer = setInterval(function ()
        {
            next.onclick();
        }, 2000);
    }
    function stop()
    {
        clearInterval(timer);
    }
    play();
    container.onmouseover = stop;
    container.onmouseout = play;
}
 </script>

总结

其实写这种技术性文章最能考验你是否理解了这个特效,同时也是最累的。昨天晚上我在跟着慕课网做的时候,有一瞬间想放弃了,毕竟现在框架那么多,何必那么绞尽脑汁用一个复杂的函数做动画呢,直接引用JS库多省事。但是直到我写完这篇教程,我很庆幸我坚持下来了。这是我的第一篇文章,简书提供了一个很棒的平台来记录一个平凡人的世界。以后会常来这里,分享自己的所得,这也是一种喜悦。

日记本
Gupao