JS效果 - 无缝滚动/滚动加停顿
js效果中的滚动效果应该也是我们在浏览页面中比较常见的一个效果,其中不乏有文字滚动、图片滚动等。在这里我将以循序渐进的方式向大家讲解其滚动方法和原理。
首先请看效果图:
其实做此效果是不需要添加滚动轴的,但是这样却更方便于我们了解方法原理。在叙述方法之前先介绍下此效果所用的一些属性。
1、innerHTML:inner(里面,元素),innerHTML是用来改变对象DH内部的HTML语句。而此效果中我们所用到的a.innerHTML=b.innerHTML就是b将自己的HTML元素赋予a,用在这里就如克隆一说。
2、offsetWidth:是指对象包括边框在内的宽度。与clientWidth不同,clientWidth指对象不包括边框只包括内容的实际宽度。
3、scrollLeft:我们知道scroll是滚动轴,那么scrollLeft就是指滚动轴自左向右滚出时距离左边起点的距离。scroll有四个方向,大家依次类推。
了解了这三项基本属性,我们就有了初步实现滚动的思想。首先我们要知道一个块就有这么宽,如果里面的内容也和外边包裹它们的父标签宽度一样的话,那么滚动条的宽度肯定也与内容一样宽,根本就没得滚。只有在里面内容大于父标签内容并且对父标签设置完固定宽进行超出部分隐藏。这样即使在我们并没有控制js时也是可以实现手动拖拉滚动条的。了解这点后再说循环滚动,既然子标签要超出父标签才能实现滚动,那么循环滚动,循环展示一组图片不就是需要两组一模一样的图片首尾相接,当第一块的尾部滚过后第二块的首部立马相接,当然控制好精确到1px时,用户是根本察觉不到是两组图片在做滚动,因为是相同的。说完原理,再说实现办法,我们已经知道了scrollLeft的意思,那么要让图片向左滚,无疑就是增加它的数值,在我们手动拖拉滚动条时也会观察到向右拖拉时图片是想做移动的,scrollLeft的距离在不断增加。那么也就是说,要实现子标签图片的滚动,就要控制父标签的scrollLeft。再加上计时器,让父标签的scrollLeft每过多久增加多少不久实现了自动的图片滚动了吗?下面请看代码吧,在HTML的结构上也是需要做下详解的。
HTML:
<div id="div">
<table>
<tr>
<td id="td1">
<table cellspacing="0" cellpadding="0">
<tr>
<td><img src="images/wz1.jpg" alt=""/></td>
<td><img src="images/wz2.jpg" alt=""/></td>
<td><img src="images/wz3.jpg" alt=""/></td>
<td><img src="images/wz4.jpg" alt=""/></td>
<td><img src="images/wz5.jpg" alt=""/></td>
</tr>
</table>
</td>
<td id="td2"></td>
</tr>
</table>
</div>
结构上之所以使用table是因为是横向滚动,tr又具备不换行的特性,(保持不换行这点是必须的,横着滚,换行了照样没得滚)。如果想用table的结构做,我们首先想到是直接用一个table,两个tr将td里包裹成图片。但是并不能实现,首先,tr是不能设置宽高的,然而内容超过后连table也控制不了自己的宽,再使多余部分隐藏,因此外部首先嵌套的就是一层div,而内部使外部table的td内再嵌套一个table,再将内部td嵌套图片。可能大家都会觉得用table做的话嵌套层数太多代码太复杂,没有使用div做看着整洁,但是这个方法的可拓展性div是比不了的,但此方法也只用作横向滚动,竖向滚动tr便无法发挥自己的优势了,只能用div做,下面向大家附上div的结构参考。
HTML:
<div id="div1">
<div id="div2"><!-- 这里的div主要起控制不换行的作用,因为里面还要包裹两个div -->
<div id="div3">
<a href="#"><img src="images/tu1.jpg" alt=""/></a>
<a href="#"><img src="images/tu2.jpg" alt=""/></a>
<a href="#"><img src="images/tu3.jpg" alt=""/></a>
<a href="#"><img src="images/tu4.jpg" alt=""/></a>
<a href="#"><img src="images/tu5.jpg" alt=""/></a>
</div>
<div id="div4"> </div>
</div>
</div>
使用table制作还能减少css样式的设置,省去很多浮动。但是table中的td元素之间的间距是需要解决的,如以上代码中已经写到在包裹图片的table标签中加入cellspacing="0" cellpadding="0",当然也可以直接在css中这样写,table{border-collapse:collapse;},便可去除td之间的间距,图片折行引起的间距使用浮动解决,同时我们还要养成的习惯便是当子元素浮动后要对其父标签设置overflow:hidden;否则其父标签将不再包裹子标签,加border后便可看到。
css:
body {margin:0;}
#div {border:5px #0ff solid;height:160px;width:800px;overflow:hidden;overflow-x:scroll;}
#div table td {overflow:hidden}
#div img {float:left;}
js:
<script type="text/javascript">
vardiv=document.getElementById("div");//最外层div
var td1=document.getElementById("td1");//第一个td,里面包裹图片
var td2=document.getElementById("td2");//没有包裹内容(图片)的td
td2.innerHTML=td1.innerHTML;//将第一个td中的内容赋予第二个td
function hs1(){
div.scrollLeft++;//外围父标签div的scrollLeft加加(上文有详解)
}
var id=setInterval(hs1,10);//每过10毫秒加1,从而初步实现滚动
</script>
有了上述一个开端实现了图片的初步滚动,我们能观察到当滚动轴滚到右边尽头时就不再滚动了,我们又如何让它从头继续滚动呢?请看下列代码:(接上述代码做延伸)
<script type="text/javascript">
vardiv=document.getElementById("div");
vartd1=document.getElementById("td1");
vartd2=document.getElementById("td2");
td2.innerHTML=td1.innerHTML;
functionhs1(){
if(td1.offsetWidth<=div.scrollLeft){
//滚动轴停了,图片必定也滚到尽头了,这时就判断第一个或第二个(二者同宽)
//的宽度(或加边或不加边)是不是已经大于或等于父标签div的scrollLeft
div.scrollLeft=0;//如果上述条件成立,那么div的scrollLeft就从零开始
}else{
div.scrollLeft++;//不属上述情况时就继续++
}
}
var id=setInterval(hs1,10);
</script>
运行到这里就已经实现了无缝滚动,那么接下来要考虑的就是当用户鼠标放上时停止滚动,离开时继续滚动。思想也很容易理解,就是放上时清除计时器,离开时再加上计时器。其实不用事件监听的话要做到这一步只需两句话,很容易。但是为了防止事件冒泡,我们还是需要使用时间监听的方法解决。
事件冒泡:css中,子标签会继承父标签的样式。js中,由于很多浏览器是没有分层的,不能分层解析,子标签就会继承父标签的动作造成事件冒泡。所以我们要尽量阻止冒泡。
接上述代码延伸:
<script type="text/javascript">
vardiv=document.getElementById("div");
vartd1=document.getElementById("td1");
vartd2=document.getElementById("td2");
td2.innerHTML=td1.innerHTML;
functionhs1(){
if(td1.offsetWidth<=div.scrollLeft){
div.scrollLeft=0;
}else{
div.scrollLeft++;
}
}
varid=setInterval(hs1,10);
functionhs2(){
addEventHandler(div,"mouseover",function(){clearInterval(id);});
//使用事件监听的方法,如果此方法只被调用一次,就可以直接写在这里的函数段里
addEventHandler(div,"mouseout",function(){id=setInterval(hs1,10);});
}
hs2();
function addEventHandler(target,type,func){
if (target.addEventListener){
target.addEventListener(type,func,false);
}else if (target.attachEvent){
target.attachEvent("on"+type,func);
}else{
target["on"+type]=func;
}
}//事件监听(对于事件监听的理解在《详解tab切换四种方法》中有注解)
</script>
一步步实现了无缝循环滚动以及监听用户动作停止继续后,就要继续考虑另一种效果了,当图片滚动到一定位置时会自动停止,停止多久后再继续执行正常滚动,用户鼠标放上时也会停止,离开时继续。这样的效果我们都能联想到的就是先命令div的scrollLeft进行到什么位置后清掉计时器,在使用setTimeout的计时器控制其再隔多久继续进行滚动。那么,我们如何才能获取到每隔多长的距离才让其停止呢,注意是“每”我原来试过用加法,显然是行不通的,一轮一轮的无限循环要加到什么时候。但是我们同学便想到了一个很巧妙的办法,求余。求余的运算符号是%,求余也就是获取倍数,就能实现每前进多远就清掉计时器的办法。代码解析请看下方代码:
<script type="text/javascript">
vardiv=document.getElementById("div");
vartd1=document.getElementById("td1");
vartd2=document.getElementById("td2");
td2.innerHTML=td1.innerHTML;
varidd=null;//此处声明计时器idd,否则在未读取对象就有执行关于其的命令时报错没有对象。
function hs1(){
if (td1.offsetWidth<=div.scrollLeft){
div.scrollLeft=0;
}else{
div.scrollLeft++;
}
if (div.scrollLeft%160==0){
//这里便是求余的判断,每当到div的scrollLeft能整除160这个数值的位置时,(数值我们可以自己设),
clearInterval(id);//就清掉计时器。
idd=setTimeout("id=setInterval(hs1,10)",1000);
//setTimeout的计时器,控制停止过一秒时继续执行计时器id,也就是继续滚动
}
}
var id=setInterval(hs1,10);
function hs2(){
addEventHandler(div,"mouseover",function(){if(idd){clearTimeout(idd);}clearInterval(id);});
//用户鼠标放上时便又要多一句判断,如果正在运行idd计时器时也将其清掉。
addEventHandler(div,"mouseout",function(){id=setInterval(hs1,10);});
}
hs2();
function addEventHandler(target,type,func){
if (target.addEventListener){
target.addEventListener(type,func,false);
}else if (target.attachEvent){
target.attachEvent("on"+type,func);
}else{
target["on"+type]=func;
}
}//事件监听
</script>