原生JS轮播-各种效果的极简实现(二)面向对象版本的实现和优化
之前写了一篇原生JS轮播,不过是非面向对象的,并且也没有添加上自动轮播。这里来写一下如何优化和进阶。
这里简单地介绍一下之前的代码,这是html结构
<body> <div class="wrap"> <ul class="pic-group"> <li><img src="./pic1.jpg" alt=""></li> <li><img src="./pic2.jpg" alt=""></li> <li><img src="./pic3.jpg" alt=""></li> <li><img src="./pic4.jpg" alt=""></li> <li><img src="./pic5.jpg" alt=""></li> </ul> <ol class="num-group"> <li class="current">1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ol> </div> </body>
css结构
<style type="text/css"> * { margin: 0; padding: 0; } ul, li { list-style: none; } body { background-color: #000; } .wrap { position: relative; border: 8px solid #fff; margin: 100px auto; width: 400px; height: 250px; overflow: hidden; } .pic-group { position: absolute; } .pic-group li img { display: block; width: 100%; height: 100%; } .num-group { position: absolute; bottom: 5px; right: 0; height: 20px; } .num-group li { float: left; width: 20px; height: 20px; line-height: 20px; font-size: 10px; margin-right: 10px; background-color: #089e8a; color: #eee; text-align: center; border-radius: 10px; cursor: pointer; opacity: 0.8; } .num-group li:hover { box-shadow: 0 0 18px #000; } .num-group li.current { opacity: 1; color: #089a8a; background-color: #000; } </style>
JS 结构
const oImage = document.getElementsByClassName("pic-group")[0]; const oNumber = document.getElementsByClassName("num-group")[0]; const numList = oNumber.getElementsByTagName("li"); for (let i = 0; i < numList.length; i++) { numList[i].onmouseover = () => { clearNumState(); show(i); } } const clearNumState = () => { for (const item of numList) { item.className = ""; } } let timer = null; const show = (index) => { numList[index].className = "current"; if (timer) clearInterval(timer); const target = -250 * index; let now = oImage.offsetTop; timer = setInterval(() => { if (now === target) { clearInterval(timer); } else { now += move(target); oImage.style.top = now + "px"; console.log(oImage.offsetTop); } }, 20); } const move = (target) => { // 当前值 const now = oImage.offsetTop; const differ = target - now; console.log(differ / 10); const res = differ > 0 ? Math.ceil(differ / 10) : Math.floor(differ / 10); return res; }
进阶1:我们可以通过offsetHeight
获取图片元素的高度。好处1是我们的移动函数不用限死。也为后来将其变为通用的一组代码做铺垫。
这里遇到一个不该出现的问题。常常我们看到直接获取高度的时候是 0,原因是:一些元素是根据内容的高度自动调整的,即所谓的自适应高度,它的高度取决于内容的多少。
获取这样的自适应高度
元素主要有两种办法
- currentStyle
- offsetHeight
但用在这里我的offsetHeight
竟然是0?不是说好了能获取自适应高度的吗!
const oHeight = oImage.getElementsByTagName("li")[0].offsetHeight; console.log(oHeight); // 0 !!! 0 !!!
经过我的发现,我知道了问题出在这里?JS代码跑完了?但是图片是否加载完毕了呢?
window.onload = function() { const oT = document.getElementsByClassName("pic-group")[0]; const oI = oT.getElementsByTagName("li"); console.log(oI[0].offsetHeight); // 250 }
图片没有加载完。当然这里还有一个疑问,为什么控制台有了答案?(这个留待以后探究)
https://www.cnblogs.com/hanxingli/p/5513116.html
这里第 5 句 正是解答,图片还在加载中,先执行了代码。
那么,解决方案是什么呢?
我们需要的是等图片加载完后执行JS的办法
http://www.jb51.net/article/79233.htm
思前想后,这只是个简单的轮播,我选择了window.onload
,还有一个不太破坏代码的是async/await
,可是那需要加载新的库支持,所以这里选择了最开始的window.onload
。
进阶2:添加自动轮播,同时,我们也可以发现,clearNumState
应该放在show函数
里面
const oImage = document.getElementsByClassName("pic-group")[0]; const oNumber = document.getElementsByClassName("num-group")[0]; const numList = oNumber.getElementsByTagName("li"); const imageList = oImage.getElementsByTagName("li"); const oHeight = imageList[0].offsetHeight; for (let i = 0; i < numList.length; i++) { numList[i].onmouseover = () => { // clearNumState(); show(i); } } const clearNumState = () => { for (const item of numList) { item.className = ""; } } let timer = null; const show = (index) => { clearNumState(); numList[index].className = "current"; if (timer) clearInterval(timer); const target = -oHeight * index; let now = oImage.offsetTop; timer = setInterval(() => { if (now === target) { clearInterval(timer); } else { now += move(target); oImage.style.top = now + "px"; console.log(oImage.offsetTop); } }, 20); } const move = (target) => { const now = oImage.offsetTop; const differ = target - now; console.log(differ / 10); const res = differ > 0 ? Math.ceil(differ / 10) : Math.floor(differ / 10); return res; } let autoTimer = null; let index = 0; const auto = () => { autoTimer = setInterval(() => { show(index); index < numList.length - 1 ? index++ : index = 0; }, 2000); } auto();
进阶3:还需要添加上鼠标移动前后的东西
oImage.onmouseover = function() { if (autoTimer) clearInterval(autoTimer); } oImage.onmouseout = function() { auto(); }
进阶4:细节优化:关于我们若是我们刚好按到当前显示的轮播页,这个时间问题,就跟下面一起写了~
进阶5:最后就是将代码模式变为面向对象的模式,使得同一页面上的多个轮播结构能够一起进行。这里的改动比较大,就直接上代码了。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> * { margin: 0; padding: 0; } ul, li { list-style: none; } body { background-color: #000; } .wrap { position: relative; border: 8px solid #fff; margin: 100px auto; width: 400px; height: 250px; overflow: hidden; } .pic-group { position: absolute; } .pic-group li img { display: block; width: 100%; height: 100%; } .num-group { position: absolute; bottom: 5px; right: 0; height: 20px; } .num-group li { float: left; width: 20px; height: 20px; line-height: 20px; font-size: 10px; margin-right: 10px; background-color: #089e8a; color: #eee; text-align: center; border-radius: 10px; cursor: pointer; opacity: 0.8; } .num-group li:hover { box-shadow: 0 0 18px #000; } .num-group li.current { opacity: 1; color: #089a8a; background-color: #000; } </style> </head> <body> <div class="wrap"> <ul class="pic-group"> <li><img src="./pic1.jpg" alt=""></li> <li><img src="./pic2.jpg" alt=""></li> <li><img src="./pic3.jpg" alt=""></li> <li><img src="./pic4.jpg" alt=""></li> <li><img src="./pic5.jpg" alt=""></li> </ul> <ol class="num-group"> <li class="current">1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ol> </div> </body> <script type="text/javascript"> var Carousel = function(cname) { this.init(cname); } Carousel.prototype = { init: function(cname) { let me = this; me.oWrap = document.getElementsByClassName(cname)[0]; me.oImage = me.oWrap.getElementsByTagName("ul")[0]; me.imageList = me.oImage.getElementsByTagName("li"); me.imageHeight = me.imageList[0].offsetHeight; me.oNumber = me.oWrap.getElementsByTagName("ol")[0]; me.numberList = me.oNumber.getElementsByTagName("li"); me.index = 0; me.moveTimer = null; me.autoTimer = null; for (let i = 0; i < me.numberList.length; i++) { me.numberList[i].onmouseover = function() { me.index = i; me.Show(i); } } me.Auto(); me.oImage.onmouseover = function() { clearInterval(me.autoTimer); } me.oImage.onmouseout = function() { me.Auto(); } }, Show: function(index) { let me = this; me.clearClass(index); me.Move(index); }, Move(index) { let me = this; let target = -index * me.imageHeight; let now; let speed; if (me.moveTimer) clearInterval(me.moveTimer); me.moveTimer = setInterval(() => { now = me.oImage.offsetTop; speed = (target - now) / 10; speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed); now === target ? clearInterval(me.moveTimer) : me.oImage.style.top = now + speed + "px"; }, 20); }, Auto() { let me = this; me.autoTimer = setInterval(() => { me.index < me.imageList.length - 1 ? me.index++ : me.index = 0; me.Show(me.index); }, 2000); }, clearClass(index) { let me = this; for (let i = 0; i < me.numberList.length; i++) { me.numberList[i].className = ""; } me.numberList[index].className = "current"; } } window.onload = function() { new Carousel("wrap"); } </script> </html>