原生JavaScript无缝轮播图特效

什么是轮播图?

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

高大上的无缝轮播

本轮播图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库多省事。但是直到我写完这篇教程,我很庆幸我坚持下来了。这是我的第一篇文章,简书提供了一个很棒的平台来记录一个平凡人的世界。以后会常来这里,分享自己的所得,这也是一种喜悦。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,825评论 25 707
  • 通过学习,我理解了图片轮播原理,学习了setTimeout()、setInterval()函数设置定时器与清除定时...
    McRay阅读 2,141评论 0 7
  • 进入前端将近一年了,js还是很弱,突发奇想写一个轮播图,就找到了这个博主的材料,和大家分享。 轮播图的原理: 一系...
    FRRRR阅读 3,657评论 0 11
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 寿司店的老板娘(一)寿司店的老板娘(二) (续上)有一天,我们犹豫着晚上吃什么。 “我们去找老板娘吧”“好啊” 于...
    珉思苦想阅读 842评论 0 0