1. 首页
  2. 程序人生

JS+定时器实现图片轮播

图片轮播效果在网页中的使用频率越来越高,特别适用于网站首页展示重要事项或广告时使用,得到了各网站的普遍采纳。目前网络上也有越来越多的基于JavaScript框架的轮播组件可供我们选择,这样我们只需要设定好轮播图片的地址和核心参数后即可完成一个轮播的效果。而本项目则使用原生JavaScript,不借助于任何组件和框架来完成的一个图片轮播效果,如图11-5所示。

图片2.png

图11-5 图片轮播效果

开发思路

相信大家通过对各轮播效果的观察,会发现其实质就是几张相同尺寸的图片,在一定的时间内不停地进行平滑的切换显示而已。所以要实现这样的效果,我们首先必须要解决的问题就是图片的切换,进而再解决平滑切换,最后再实现手工切换效果。所以我们将本项目的开发思路进行一个分解,一步一步来实现其效果。

首先,我们先来解决固定时间内图片切换的问题,单纯就这一问题来说,思路非常简单,就是设置一个定时器,然后在固定时间周期内改变图片的地址,从而实现类似轮播的效果。但是这种方案的缺点在于图片是瞬间变化的,并不会看到平滑移动的过程。所以无法从根本上解决视觉效果和用户体验。

那么接下来我们来看看第二种解决方案,通过设定一组无序列表,将N张轮播的图片附加于列表项里面,并保持该列表水平展示。然后在该列表的外面套上一个DIV容器,并设置该窗口的宽度和高度刚好是一张图片的高度与宽度。进而设置该DIV的overflow: hidden将无法展示的其他图片隐藏起来。然后再使用绝对定位的方式在固定的时间内将列表向左或向右移动一张图片的大小,进而实现切换。但是光有这样的切换,我们看上去的效果跟上面的这种直接修改图片地址的视觉效果是类似的,就是图片瞬间就切换了。所以我们还需要控制图片切换时的移动速度,可以给定一个移动的时间,而不是瞬间移动,这样的实现了类似平滑移动的效果。

实现平滑移动的效果有两种可供选择的方案:一种是使用定时器,实现极短的时间移动极端的距离,多移动一些次数进而实现平滑。比如图的宽度是800像素,我们可以实现每5毫秒移动5个像素,移动160次即可。另外一种方案,我们可以使用CSS的Transition来实现平滑移动,只需要设定Transition的关键属性和移动距离即可。

最后,我们再来看看手工切换图片,这也是图片轮播效果的标配。通过,当我们将鼠标移动到轮播图上时,会在图片上出现一个向左和向右的切换按钮,在图11-5中我们也可以看到该按钮。此时我们直接在上面点击,可以忽略定时器直接对轮播图片进行切换。事实上,要实现这个效果,只需要对已经实现的移动效果进行调用即可。所以,我们在设计时,应该将每一个动作放在单独的函数当中,这样更便于代码的重用。另外一方面,我们可以通过响应该DIV容器的onmouseover和onmouseout事件来对两个切换按钮进行显示和隐藏。这里需要注意一下的是我们不能直接使用:hover伪类来实现鼠标悬停的效果,因为这种悬停效果只能针对其自身的CSS属性进行改变,无法去影响到其他元素。

代码实现

针对第一种解决方案,直接利用定时器对图片的地址进行修改进而实现切换,虽然不够平滑,视觉效果较差,但是也不失为一种简单的解决方案。针对该方案的代码我们不作过多解释,大家直接参考代码即可:

<!DOCTYPE html>
<html>
<head>
<meta charset=”UTF-8″>
<title></title>
<script>
setInterval(function(){
var myimg = document.getElementById(“loopimg”);
if (myimg.src.match(“videoonline.png”)) {
myimg.src “../image/woniufamily.png”;
}
else if (myimg.src.match(“woniufamily.png”)) {
myimg.src “../image/snow-night.jpg”;
}
else {
myimg.src “../image/videoonline.png”;
}
}, 3000);
</script>
</head>
<body>
<div style=”width: 800pxheight: 50pxfont-size: 30pxmarginauto;>图片轮播展示效果</div>
<div style=”width: 800pxheight: 450pxmarginauto”>
<img src=”../image/videoonline.png” id=”loopimg” width=”800″ height=”450″/>
</div>
</body>
</html>

 

上述代码中需要注意的是,通过分支结构的方式来对图片切换比较适用于图片比较少的时候,如果图片较多的话,建议大家使用循环的方式,将所有要进行切换的图片命名为同一个系列,通过不同的序号来进行区分。这样我们在循环中可以直接通过生成一个序号的方式来完成图片地址的修改。这才是比较灵活处理的代码,其简洁度和适用性会更高。

接下来我们继续来看如何实现第二种解决方案:利用列表项并进行位置移动的方式完成轮播效果。首先,我们先将页面的基本元素进行布局,此处我们仍然只设计三张图片,如果图片有更多张,其思路没有任何变化,所以不影响实际效果。布局代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8″>
<title>JS幻灯代码</title>
<style>
#outer {
width: 800px;
height: 450px;
bordersolid 2px red;
margin: 0 auto;
}
#main {
width: 800px;
height: 450px;
overflowhidden;
positionabsolute;
}
img {
width: 800px;
height: 450px;
}
ul {
margin: 0;
padding: 0;
list-stylenone;
positionabsolute;
top: 0px;
left: 0px;
width: 2400px;
height: 450px;
}
ul li {
list-stylenone;
floatleft;
}
#prev {
width: 40px;
height: 60px;
background-colorrgba(160,220,240,0.8);
font-size: 40px;
font-weightbold;
positionabsolute;
left: 10px;
top: 45%;
z-index: 100;
text-aligncenter;
line-height: 60px;
displaynone;
}
#prev:hover {
displayblock;
}
#next {
width: 40px;
height: 60px;
background-colorrgba(160,220,240,0.8);
font-size: 40px;
font-weightbold;
positionabsolute;
right: 10px;
top: 45%;
z-index: 100;
text-aligncenter;
line-height: 60px;
displaynone;
}
#next:hover {
displayblock;
}
</style>
</head>
<body onload=”setInterval(startMove, 5000);>
<div style=”width: 800pxheight: 50pxfont-size: 30pxmarginauto;>
图片轮播展示效果
</div>
<div id=”outer”>
<div id=”main”>
<div id=”prev” onclick=”moveToPrev()><</div>
<div id=”next” onclick=”moveToNext()>></div>
<ul id=”box” onmouseover=”showButton()

onmouseout=”hideButton()>
<li><img src=”../image/videoonline.png” /></li>
<li><img src=”../image/woniufamily.png” /></li>
<li><img src=”../image/snow-night.jpg” /></li>
</ul>
</div>
</div>
</body>
</html>

针对上述的布局代码,需要给大家强调一个小细节。就是我们既然已经使用了在图片容器的响应onmouseover和onmouseout事件让切换按钮的显示和隐藏,为什么还要对两个按钮设置:hover属性继续将其设置为显示状态呢?因为一旦我们把鼠标放到按钮上时,其实同时也响应了图片容器的onmouseout事件,所以如果不额外进行设置,我们将无法让鼠标在按钮上进行点击,因为鼠标一放到按钮上,按钮就会被隐藏。

布局完成后,我们已经可以看到正常在页面中显示了第一张图片,另外两张图片被隐藏。接着我们来实现图片的切换和平滑移动的效果。此处我们先使用定时器的方式来实现,整合后的代码如下,详细解释请参考代码的备注:

<script>
var moveToNextCount = 0;  // 实现平滑移动到下一张图片的次数
    var moveToPrevCount = 0;  // 实现平滑移动到上一张图片的次数
    var moveToStartCount = 0; // 从最后一张回到第一张的平滑移动次数

    function startMove() {
// 每次开始移动时,都再对三个变量重新初始化一遍
        moveToNextCount = 0;
moveToPrevCount = 0;
moveToStartCount = 0;

var box = document.getElementById(“box”);
// 1600相当于列表项已经移动了2张图片,正在第3张图片上
        // 所以此处想要说明的是图片已经移动到了最后一张上
        if (box.offsetLeft <= -1600) {
moveToStart();  // 返回到第一张图片上
        }
else {
moveToNext();   // 移动到下一张图片上
        }
}

// 移动到下一张图上
    function moveToNext() {
moveToNextCount++;
var box = document.getElementById(“box”);
// 由于每次只移动5像素,所以需要移动160
        if (moveToNextCount < 161) {
box.style.left = box.offsetLeft – 5 + “px”;
// 循环调用本函数,实现每5毫秒移动一次
            setTimeout(moveToNext, 5);
}
}

// 移动到上一张图上
    function moveToPrev() {
moveToPrevCount++;
var box = document.getElementById(“box”);
if (moveToPrevCount < 161) {
box.style.left = box.offsetLeft + 5 + “px”;
setTimeout(moveToPrev, 5);
}
}

// 移动到第一张图片上
    function moveToStart() {
moveToStartCount++;
var box = document.getElementById(“box”);
if (moveToStartCount < 81) {
box.style.left = box.offsetLeft + 20 + “px”;
setTimeout(moveToStart, 5);
}
}

// 显示切换按钮
    function showButton() {
document.getElementById(“prev”).style.display “block”;
document.getElementById(“next”).style.display “block”;
}

// 隐藏切换按钮
    function hideButton() {
document.getElementById(“prev”).style.display “none”;
document.getElementById(“next”).style.display “none”;
}
</script>

将上述代码进行整合后,我们已经初步完成了轮播的效果。上述代码中设置三个全局变量的目的只是为了在实现平移的过程中用于记录移动的次数,否则setTimeout()会不停地死循环的调用自身函数,进而无法让移动过程停下来。整个移动过程,只是通过修改元素的left属性实现左右的定位和移动,所以只要抓住了这几个关键点,相信大家对代码的理解是比较容易的。

另外,上述代码虽然效果实现了,但是有一个严重的问题我们可能没有怎么注意到,就是所有的坐标,长度,循环次数这些都是写死的(Hard-Code)。这样会出现的问题就是如果我们的图片不是800像素的宽度,那么代码中的绝大部分数字都得重新修改,而且我们还得先计算出来这些数值才能修改代码,一点都不智能,适应性极差。所以我们需要对上述的代码部分进行优化,增强其自适应能力。

同时,本次优化时,我们也顺便利用CSS3的过渡效果Transition来实现图片的平滑移动,让代码变得更加的简洁。虽然Transition的本质也是极短的时间进行短距离的移动,跟我们上面实现代码的逻辑和原理没有本质区别。要说区别,利用Transition来进行平滑移动唯一多的特点就是实现了移动过程的加速减速而已,而这种效果在短时间内,肉眼的感觉并不明显,当然也不是我们这个项目需要探讨的话题。一起来看看优化后的代码:

<script>
var imgCount = 4;   // 图片的数量
    var imgWidth = 800; // 图片的宽度

    function startMove() {
var box = document.getElementById(“box”);
if (box.offsetLeft <= -1*imgWidth*(imgCount-1)) {
moveToStart();
}
else {
moveToNext();
}
}

// 移动到下一张图上
    function moveToNext() {
box.style.transition “all 2s ease-in-out”;
box.style.left = box.offsetLeft – imgWidth “px”;
}

// 移动到上一张图上
    function moveToPrev() {
box.style.transition “all 2s ease-in-out”;
box.style.left = box.offsetLeft imgWidth “px”;
}

// 移动到第一张图片上
    function moveToStart() {
box.style.transition “all 2s ease-in-out”;
box.style.left = box.offsetLeft + (imgCount-1)*imgWidth “px”;
}

// 显示切换按钮
    function showButton() {
document.getElementById(“prev”).style.display “block”;
document.getElementById(“next”).style.display “block”;
}

// 隐藏切换按钮
    function hideButton() {
document.getElementById(“prev”).style.display “none”;
document.getElementById(“next”).style.display “none”;
}
</script>

上述代码使用了Transition属性直接设定在2秒内完成图片的平移,移动的距离为图片的宽度。其效果跟前一种方案是完全一致的。跟我们基于原理推测的结果一模一样。另外,我们也通过两个全局变量的定义和引用,让代码变得更加灵活。如果我们需要增加一些图片,或者调整图片的宽度的话,我们只需要修改这两个全局变量的值即可,代码的其他部分不需要做任何修改。

思维拓展

如果我们仔细对上述代码进行测试的话,会发现仍然存在问题,比如当我们进行手工切换时,刚好遇到定时器也准备切换。而手工切换和定时器切换的方向刚好又是反的,这个时候就会出现图片往哪边都移不动的情况。当然,针对这种情况,我们通常的解决方案是:当鼠标悬停在图片上时,通过响应onmouseover的事件直接调用clearInterval停止定时器,转为手工移动。当鼠标移出图片区域时,又将该定时器启动即可。

图片轮播是我们现在的网站当中应用非常广泛的一种技术。通常情况下,前端开发工程师并不会直接使用原生的JavaScript来完成图片轮播的效果,而是利用JQuery框架再加上图片轮播组件来完成。事实上,这样的处理虽然实现了效果,完成了任务,但是对于学习前端开发来说是没有任何帮助,甚至是有很大害处的。这样就会导致我们的前端开发工程师只知其然,不知其所以然,对原理层面没有任何理解,只是一个代码搬运工而已,这是笔者奉劝大家的很重要的一点。所以在本书中,我们所有的代码全部使用原生JavaScript来处理,不借助于任何第三方框架,其目的也在于此,让大家从根本上理解代码,理解其工作原理。

当然,框架之所以这么流行,除了JQuery以外,Angular,Reat,EasyUI等JavaScript前端框架都非常受前端开发工程师的青睐。其主要原因是:一方面没有太多工程师原理去深入研究一些很细致的东西,因为这并不能保证他在短期内就能成为一个高手;而更重要的一方面,框架对于提高项目的开发效率是非常有帮助的,而且当我们的前端不仅仅只是为了前端展现,还需要与后端进行数据交互时,框架就会变得更加重要。但是无论怎样,笔者都希望大家能够自己尝试着去实现这些框架当中常见的功能,或者是将这些框架的代码去学习一遍,把框架背后的设计原理和实现思路都梳理一遍,这也是一种提高的方式。

事实上,网页上的各种特效基本上都是利用JavaScript结合CSS3动画来实现的,只是如何去结合,什么样的特效更加吸引用户的注意,这些就需要我们在实际项目中再慢慢地摸索。

本文来自投稿,不代表程序员编程网立场,如若转载,请注明出处:http://www.cxybcw.com/187340.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code