原生js轮播图
前言
轮播图在前端中的应用场景非常多、应用频率非常高,大到网站商城,小到个人主页,都会有用到轮播图的时候。
现在网上的轮播图插件也非常多,花样花式也各异,有基于jq的、基于框架,所以一般是不用我们去手写轮播图的。
但在某些情况下,我们还是需要去手写轮播图。
手写原生js轮播图,有助于我们知道轮播图的实现原理。
知道了原理,有时候我们也能根据自己的需求去修改下载下来插件的源码。
效果
我们先来看下轮播图的最终效果图:
根据上面的效果图,我们来总结一下我们要实现的轮播图的功能:
- 箭头点击
- 按钮点击
- 滑动过渡
- 自动播放
原理
轮播图原理分析:
轮播图的原理要配合html结构和css样式来讲。
我们先来看一下html结构,注意力主要集中在画红圈的地方:
上图是我们整个轮播图的html结构,可以看出代码是很少的。
可以看到,我实现的轮播图用了4张图片,但代码中有6张,我用代码注释标记的2张图片是辅助图,用来辅助动画过渡,下面会讲到。
请注意观察 slide-wrapper 和 slide-imgs 。
另外可以发现 class="slide-imgs" 元素上有样式属性 left ,在html里写样式是为了方便我们待会在js中取该元素的 left 的值。
接下来我们配合在浏览器里的效果(按f12打开控制台)来讲解轮播图的原理:
从图片中可以看出,我们轮播图每张图片的宽度是400px,同样的,整个轮播图容器 .slide-wrapper 的宽度也是 400px,当然高度也是一样。
上面的图片是 .slide-imgs 元素,它是包裹我们所有图片的容器,它的宽度是2400px,这是因为我们有6张图片,每张图片是400px(上图因为太长我没有截出)。
接着就是设置 .slide-wrapper 元素的样式,将轮播图最外层的包裹元素 overflow: hidden; :
轮播图的原理就是让“包裹图片的容器元素”(.slide-imgs元素)做位置偏移。
每次偏移的距离是每张图片的宽度。
每次切换的时候,我们就重新改变 .slide-imgs元素 的样式属性 left 的值(将.slide-wrapper设置为相对定位,将.slide-imgs设置为绝对定位)。
从上图就可以看到所有图片的包裹元素进行了位置偏移,这就是轮播图的核心原理了。
箭头点击
(先看画红圈的地方,最后会给源码)
首先获取我们两个箭头按钮的元素,编写点击后要执行的代码,
每一次点击,就切换一张图片,切换的大小是图片的宽度,这里是400px。
左点击就传入负值,右点击就传入正值,然后执行图片切换的函数 imgchange() :
(先看画红圈的地方,最后会给源码)
imgchange()函数的作用是:
利用的传入的偏移值,计算出 .slide-imgs元素 该具有的新的位置值(left值),值计算出来后,再设置 . slide-imgs元素 的样式属性。
当向左切换,切换超出第一张图片的时候,将 .slide-imgs元素 的位置赋值为最后第四张图片的位置值;
当向右切换,切换超出第四张图片的时候,将 .slide-imgs元素 的位置赋值为最后第一张图片的位置值(如上面的if语句),这样就能实现循环切换。
按钮点击
当点击按钮的时候我们要做两件事情:
1.切换到对应的图片;
2.改变按钮的样式。
要做到这两件事我们就需要一个值 slide_index 来记住当前图片和圆点的位置索引。
主要看红圈内的代码,我们先完成第一件事:切换到对应的图片。
从html代码可以看出,我们给每一个按钮都赋予了相应的位置索引值 data-index。
接着,我们再给每一个按钮添加点击事件,当点击按钮的时候,我们再调用imgchange()函数,并传入正确的偏移量的值。
因为点击按钮的时候是可以跳跃性地点击,比如点第一个按钮后点第二个按钮,但也可以点第一个按钮后就点第四个按钮,
所以传入的偏移量通过这句代码来计算:
// data_index是点击的按钮的位置索引
// slide_index是当前图片的位置索引
var offset = -400 * (data_index - slide_index);
所以当我们执行imgchange()函数后要记得当前图片的索引值:
slide_index = data_index;
最后是完成第二件事:改变按钮的样式。
事先将选中按钮的样式 .active 写好:
再改变选中按钮的类名(classname),赋予它 active 。
前提,我们要先利用循环清除每一个按钮的 .active ,这样才不会出现多个选中按钮。
滑动过渡
上面我们所实现都效果都是一瞬间的,就是一瞬间就切换到下一张图片,并没有一种过渡的视觉体验。
如何实现这种滑动过渡的效果?
我们知道,轮播图的核心原理是位置偏移,就是从一个位置到另一个位置的改变,
如果我们为这种改变加上一段时间,假设这个时间是2s,那么完成这个变化就得花2s时间,就不是一瞬间完成了,
这个完成过程就是过渡,这个过渡因为时间够长,所以能被我们所观察到。
好了,现在我们有一段时间 time 了,但还不够,
因为滑动的实质是慢慢地偏移,将一段完整的偏移量分为几次小的偏移量,
假设我们的 time = 500ms,我们每一次所花时间 pertime = 10ms,
所以在500ms内,我们要偏移多少次 per = time / pertime ,
每一次偏移的距离为 peroffset = offset / per 。
一直地偏移,如果还没到达偏移的目标量时,还是继续偏移,
这里我们用一个延时定时器和递归来达到这个效果,每 10ms 就进行一次偏移。
两张辅助图
如果没有设置辅助图做衔接的话,假设从第四张图滑向第一张图,第四张图会先滑向一片空白区域:
而不是
自动播放
用一个定时器自动地去调用箭头点击函数:
源码
<!doctype html> <html> <head> <meta charset="utf-8"> <title>原生js轮播图</title> <style> .slide-wrapper { position: relative; width: 400px; height: 250px; overflow: hidden; } .slide-imgs { position: absolute; z-index: 1; width: 2400px; height: 250px; } .slide-img { float: left; width: 400px; height: 250px; } .slide-btns { position: absolute; right: 0; bottom: 5px; left: 0; z-index: 2; width: 60px; margin: auto; } .slide-btn { display: inline-block; float: left; width: 10px; height: 10px; margin: 0 2px; background-color: #fff; border-radius: 50%; cursor: pointer; } .active { background-color: #20217e; } .slide-arrow { position: absolute; top: 0; bottom: 0; z-index: 2; width: 23px; height: 23px; margin: auto; color: #fff; font-size: 30px; font-weight: bold; line-height: 23px; text-decoration: none; } #slide_left { left: 5px; } #slide_right { right: 5px; } </style> </head> <body> <div class="slide-wrapper" id="slide_wrapper"> <div class="slide-imgs" id="slide_imgs" style="left: -400px;"> <img class="slide-img" src="slide-img3.jpg"><!-- 辅助过渡图 --> <img class="slide-img" src="slide-img0.jpg"> <img class="slide-img" src="slide-img1.jpg"> <img class="slide-img" src="slide-img2.jpg"> <img class="slide-img" src="slide-img3.jpg"> <img class="slide-img" src="slide-img0.jpg"><!-- 辅助过渡图 --> </div> <div class="slide-btns" id="slide_btns"> <span class="slide-btn active" data-index="0"></span> <span class="slide-btn" data-index="1"></span> <span class="slide-btn" data-index="2"></span> <span class="slide-btn" data-index="3"></span> </div> <a class="slide-arrow" id="slide_left" href="#"><</a> <a class="slide-arrow" id="slide_right" href="#">></a> </div> <script> var slide_left = document.getelementbyid('slide_left'), slide_right = document.getelementbyid('slide_right'), slide_imgs = document.getelementbyid('slide_imgs'), slide_btns = document.getelementbyid('slide_btns').getelementsbytagname('span'), slide_wrapper = document.getelementbyid('slide_wrapper'), slide_index = 0, // 当前图片或圆点的索引 animated = false, // 标记是否处于动画状态 timer = null; function imgchange(offset) { var newleft = parseint(slide_imgs.style.left) + offset, // 新坐标值 time = 500, // 总移动时间 pertime = 10, // 每次移动的时间 per = time / pertime, // 移动多少次 peroffset = offset / per; // 每次移动的距离 animate = function() { animated = true; // 注意这里的parseint(slide_imgs.style.left)是一个实时的值,不能赋给一个变量以节省代码 if ((peroffset < 0 && parseint(slide_imgs.style.left) > newleft) || (peroffset > 0 && parseint(slide_imgs.style.left) < newleft)) { slide_imgs.style.left = parseint(slide_imgs.style.left) + peroffset + 'px'; settimeout(animate, pertime); } else { animated = false; slide_imgs.style.left = newleft + 'px'; if (newleft > -400) { slide_imgs.style.left = -1600 + 'px'; } if (newleft < -1600) { slide_imgs.style.left = -400 + 'px'; } } }; animate(); } function btnchange() { for (var i=0; i<slide_btns.length; i++) { if (slide_btns[i].classname = 'slide-btn active') { slide_btns[i].classname = 'slide-btn'; } } slide_btns[slide_index].classname = 'slide-btn active'; } function autoplay() { timer = setinterval(function() { slide_right.onclick(); }, 2000); } function stopplay() { clearinterval(timer); } slide_left.onclick = function() { if (!animated) { // 防止快速点击后出现bug imgchange(400); if (slide_index == 0) { slide_index = 3; } else { slide_index -= 1; } btnchange(); } }; slide_right.onclick = function() { if (!animated) { imgchange(-400); if (slide_index == 3) { slide_index = 0; } else { slide_index += 1; } btnchange(); } }; for (var i=0; i<slide_btns.length; i++) { slide_btns[i].onclick = function() { if (this.classname != 'slide-btn active') { var data_index = parseint(this.getattribute('data-index')); var offset = -400 * (data_index - slide_index); if (!animated) { imgchange(offset); slide_index = data_index; btnchange(); } } }; } slide_wrapper.onmouseover = stopplay; slide_wrapper.onmouseout = autoplay; autoplay(); </script> </body> </html>
优化
实现的轮播图并不是配置。
当我们改变图片的数量、width或height等时,都需要改动源码里多处地方,这样实在不方便。
至于如何优化就让大家自己去思考了。
参考视频:https://www.imooc.com/learn/18
上一篇: 大数据是信息安全产业最有前景的新机遇
下一篇: 夏天慎给孩子吃冰西瓜 易致扁桃体发炎