YAOHAIXIAO.COM

HTML(5),CSS(3),JavaScript,DOM,Ajax,JSON,Front-end technologies & Yaohaixiao

热门标签:JavaScript Performance 前端开发 前端性能优化 原创

Rss

Home » Frontend » JavaScript » DOM » JavaScript DOM编程实例教程 – 垂直无缝滚动图片

JavaScript DOM编程实例教程 – 垂直无缝滚动图片

垂直无缝滚动图片的效果是一个经常用到的效果,今天我就来跟大家实例讲解一下这个效果的实现原理,首先我们看看演示效果:

演示地址:http://www.yaohaixiao.com/scripts/scrollnews/
下载地址:http://www.yaohaixiao.com/downloads/scrollnews.rar

在讲原理之前,我先把代码贴出来,JavaScript代码如下:

// scrollver.js
// 代码来源:yahoo.com.cn
scrollVertical.prototype.scrollArea=null;      // 滚动的区域
scrollVertical.prototype.scrollMsg=null;       // 要滚动的内容
scrollVertical.prototype.unitHeight=0;         // 单独一行滚动内容的高度(程序中通过过的要滚动行的一个节点的offsetHeight获得)
scrollVertical.prototype.msgHeight=0;          // 整个滚动内容的高度
scrollVertical.prototype.copyMsg=null;         // 复制滚动内容(程序中使用复制scrollMsg.innerHTML来获得的)
scrollVertical.prototype.scrollValue=0;        // 滚动的值
scrollVertical.prototype.scrollHeight=0;       // 滚动高度
scrollVertical.prototype.isStop=true;          // 停止滚动
scrollVertical.prototype.isPause=false;        // 暂停滚动
scrollVertical.prototype.scrollTimer=null;     // 滚动计时器
scrollVertical.prototype.speed=2000;           // (默认)滚动的时间间隔2秒

/**
* @method isMoz - 判断是否为Mozilla系列浏览器
*/
scrollVertical.prototype.isMoz = function(){
return navigator.userAgent.toLowerCase().indexOf('gecko') > 0;
};

/**
* @method play - 滚动信息的处理方法(函数)
* @param {Object} o - 滚动类
*/
scrollVertical.prototype.play = function(o){
	var s_msg = o.scrollMsg;
	var c_msg = o.copyMsg;
	var s_area = o.scrollArea;
	var msg_h = o.msgHeight;

	var anim = function(){
		// 如果已经开始计时(间隔时间执行向上滚动),
		// 就停止它(以免无限制执行,耗系统资源)。
		if (o.scrollTimer) {
			clearTimeout(o.scrollTimer);
		}
		// 如果暂停了滚动(鼠标放到了滚动层上)
		// 开始以10毫秒的时间间隔运行滚动
		if (o.isPause) {
			o.scrollTimer = setTimeout(anim, 10);
			return;
		}
		// 当显示完所有信息后(这时滚动的距离就等于要滚动信息的高度msg_h)
		// 这时又重新开始滚动,将滚动距离清零
		if (msg_h - o.scrollValue <= 0) {
			o.scrollValue = 0;
		}
		else {
			o.scrollValue += 1;
			o.scrollHeight += 1;
		}
		// 根据浏览器的不同,处理滚动
		if (o.isMoz) { // Mozilla引擎浏览器
			s_area.scrollTop = o.scrollValue;
		}
		else { // 其余的浏览器则使用控制<span class="bm_keywordlink"><a href="http://www.yaohaixiao.com/?cat=4" target="_blank">CSS</a></span>样式处理滚动
			s_msg.style.top = -1 * o.scrollValue + "px";
			c_msg.style.top = (msg_h - o.scrollValue) + "px";
		}
		// 滚动高度等于显示滚动区域高度时(滚动完一行,一行内容全部显示)
		// 暂停4秒中,然后再开始执行下依次滚动。
		if (o.scrollHeight % s_area.offsetHeight == 0) {
			o.scrollTimer = setTimeout(anim, o.speed);
		}
		else {
			// 在两行内容之间过度滚动时,每10豪秒上升1px
			o.scrollTimer = setTimeout(anim, 10);
		}
	};
	// 执行滚动
	anim();
};

/**
* scrollVertical 垂直滚动的构造函数
* @param {Object} disp   -  必须  显示滚动区域的DOM节点(或节点ID)
* @param {Object} msg    -  必须  被显示的信息的DOM节点(或节点ID)
* @param {string} tg     -  可选  以什么标记为一行的标签名称(tagName)
*/
function scrollVertical(disp, msg, tg){
	// 给在之前定义的this.scrollArea付值
	if (typeof(disp) == 'string') {
		// 如果disp给的是节点的ID,通过document.getElementById获取该节点
		// 然后付值给this.scrollArea
		this.scrollArea = document.getElementById(disp);
	}
	else {
		// 如果是DOM节点,直接付给this.scrollArea
		this.scrollArea = disp;
	}
	// 以给this.scrollArea相同的方法给this.scrollMsg付值
	if (typeof(msg) == 'string') {
		this.scrollMsg = document.getElementById(msg);
	}
	else {
		this.scrollMsg = msg;
	}

	// 为了开发方便,
	// 不用一直写this.scrollMsg这么常的名字,
	// 将两个对象付给局部变量
	var s_msg = this.scrollMsg;
	var s_area = this.scrollArea;

	// 如果没有给定一行的识别标签,
	// 默认将li标签认为是一行的标签
	// 所以上面介绍的,tag参数是可选的
	if (!tg) {
		var tg = 'li';
	}

	// 获取单行的高度
	// 获取到第一(s_msg.getElementsByTagName(tg)[0])tg(一行的标签)的高度,作为单行的高度
	this.unitHeight = s_msg.getElementsByTagName(tg)[0].offsetHeight;
	// 获取整个信息框的高度
	// 公式为 单行高度(unitHeight)*行数(s_msg.getElementsByTagName(tg).length,显示信息中包含多少个tg(行)标签)
	this.msgHeight = this.unitHeight * s_msg.getElementsByTagName(tg).length;

	/*
	 * 复制要显示的信息:
	 * 连续滚动的实现其实就是通过复制信息,
	 * 并将复制的信添加到原始信息后
	 * 当原始信息滚动显示完成,就接着滚动显示复制的信息
	 * 但给人的错觉是,我们看到信息连续不断的显示
	 */
	// 创建复制内容节点
	var copydiv = document.createElement('div');
	// 这个地方感觉有点嵌妥
	// 直接使用element.id的方式,不过看上去,主流的浏览器都支持
	// 标准的DOM Core方法:
	// copydiv.setAttribute('id',s_area.id + “_copymsgid”)
	copydiv.id = s_area.id + "_copymsgid";
	// 复制原始的信息
	// 将原始的信息s_msg中的内容,直接用innerHTML写到
	copydiv.innerHTML = s_msg.innerHTML;
	// 设置复制信息节点的高度
	copydiv.style.height = this.msgHeight + "px";
	// 将复制节点添加到原始接点(scrollMsg)后
	// 其实实现的方法就是将复制信息节点(copydiv)添家到显示区域的节点(scrollArea)中
	s_area.appendChild(copydiv);

	this.copyMsg = copydiv;
	// 开始执行滚动方法
	this.play(this);
}

怎么样,看过我在脚本的注释你是否已经了解了这个效果的实现原理?其实只要你仔细看代码,你会发现实现一个效果的关键就是在于运用setTimeout方法和clearTimeout方法。

setTimeout(func,time)

setTimeout是window对象的一个方法,所以如果要是看到这么写window.setTimeout你不要感到奇怪,我们平时一般都省略了window。

setTimeout方法接受两个参数:

func – 在指定时间间隔内要执行的函数;

time – 执行函数的时间间隔(以毫秒为单位,1000毫秒等于1秒)

我一开始没有解释setTimeout的功能,而是先说了两个参数的意思,我想大家看了后就会有所了解,setTimeout的功能就是:设置定时器,在一段时间之后执行指定的代码。例如本例中的:

setTimeout(anim, o.speed);

也许你有看过类似的写法,例如:

function dosomething(){
// do something
}

setTimeOout('dosomething',1000);

个人建议不要这么写,虽然也可以正常执行,但是这样的代码的可读性太差,并且执行的效率很低。相信你看到的类似的代码一定是很久前的东西了。如果你还在新买的某本书中看到这样的写法,我想你可能很不幸地买了本烂书。现在一般我们都这么做:

function whatWeDoNow(){
	var str = "this is what we do now&";
	if (doalert) {
		clearTimeout(doalert)
	}
	var doalert = setTimeout(function(){
		alert(str);
	}, 1000);
}

不知道你发现没有,这么写还有一个好处,你的function还可以接受其他的参数,比如这里我们可以接受whatWeDoNow()函数中的局部变量。如果你再结合闭包的使用,好处会更显而易见。刚才说的将函数以字符串的形式作为setTimout的第一个参数应该说是我们要避免的不推荐的写法。好了,接下来我还要说的一个更不好的使用习惯就是只使用setTimeout()方法,而不使用clearTimeout()方法。

clearTimeout(itimeoutid)

clearTimeout()方法的功能是停止定时器,大家看上面的代码:

clearTimeout(o.scrollTimer);

Timer(定时器),够直接吧。那么为什么要停止定时器?什么时候停止呢?

为什么要停,我想用个反问:能一直不停吗,你的机器受得了吗?这里我想应该说说我们使用setTimeout的目的,我们通常使用它来实现像本例这样的动画效果。需要在很短的时间内连续不断的执行定时器,当然它是要占资源的啊。想想,只是不断的创建,而且往往我们做的处理,在1秒中内会执行很多次函数,一两次还好,上百上千次,而且一个复杂些的动画,执行很短的时间内几万次也不是没有可能事情。你想想,如果我们不在每执行完一次后销毁它。如果再加上定时器执行的函数又是个比较NB点的运算,你的宝贵的系统资源…

所以应该向我给的例子中那样,记得在每次执行了定时器后停止(销毁,释放资源)它。

function whatWeDoNow(){
	var str = "this is what we do now&";
	if (doalert) {
		clearTimeout(doalert); // clear
	}
	var doalert = setTimeout(function(){
		alert(str);
	}, 1000);
}

if (o.scrollTimer) {
	clearTimeout(o.scrollTimer); // clear
}

其实销毁的方法很简单,就是在每次创建定时器前,判断是否已经创建了订时器,就像特效例子中的:

if (o.scrollTimer) {
	clearTimeout(o.scrollTimer); // clear
}
….
….

if (o.scrollHeight % s_area.offsetHeight == 0) {
	o.scrollTimer = setTimeout(anim, o.speed);
}
else {
	o.scrollTimer = setTimeout(anim, 10);
}

逻辑如下图:

是不是一个很流畅的循环?现在大家应该知道了,为什么要clearTimeout和何时clearTimeout了吗?介绍了大半天的setTimeout和clearTimeout,现在可以看看怎么使用这个特效吧,页面代码:

< !DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
<title>滚动图片</title>
<style type=”text/css”>
< !–
*{
margin:0;
padding:0;
}
body{
text-align:center;
background-color:#FFF;
color:#000;
font:'宋体',sans-serif;
}
img{
border:0;
}
ul,li{
list-style-type:none;
}
a:link,
a:visited{
color:#000;
text-decoration:none;
}
a:hover{
color:#F00;
text-decoration:none;
}
#container{
margin:0 auto;
position:relative;
margin-top:10px;
width:720px;
height:100px;
overflow:hidden;
}
#message,
#message_copymsgid{
margin:0;
width:720px;
overflow:hidden;
}
#container ul{
float:left;
width:720px;
height:100px;
overflow:hidden;
clear:both;
}
#container li{
float:left;
text-align:center;
width:120px;
height:100px;
line-height:100px;
overflow:hidden;
padding:0;
}
#container li img{
width:96px;
height:96px;
margin-bottom:10px;
padding:1px;
border:1px solid #999;
}
–>
</style>
</head>
<body>
<div id=”container”>
<div id=”message”>
<ul><li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”听海”><img src=”img/img1.gif” alt=”听海” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”听海”><img src=”img/img1.gif” alt=”听海” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”听海”><img src=”img/img1.gif” alt=”听海” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”听海”><img src=”img/img1.gif” alt=”听海” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”听海”><img src=”img/img1.gif” alt=”听海” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”听海”><img src=”img/img1.gif” alt=”听海” /></a></li>
</ul>
<ul>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”宝贝”><img src=”img/img2.gif” alt=”宝贝” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”宝贝”><img src=”img/img2.gif” alt=”宝贝” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”宝贝”><img src=”img/img2.gif” alt=”宝贝” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”宝贝”><img src=”img/img2.gif” alt=”宝贝” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”宝贝”><img src=”img/img2.gif” alt=”宝贝” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”宝贝”><img src=”img/img2.gif” alt=”宝贝” /></a></li>
</ul>
<ul>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”因为你”><img src=”img/img3.gif” alt=”因为你” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”因为你”><img src=”img/img3.gif” alt=”因为你” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”因为你”><img src=”img/img3.gif” alt=”因为你” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”因为你”><img src=”img/img3.gif” alt=”因为你” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”因为你”><img src=”img/img3.gif” alt=”因为你” /></a></li>
<li><a href=”http://www.yaohaixiao.com/” target=”_blank” title=”因为你”><img src=”img/img3.gif” alt=”因为你” /></a></li>
</ul>
</div>
</div>
<script type=”text/javascript” language=”javascript” src=”js/event.js”></script>
<script type=”text/javascript” language=”javascript” src=”js/scrollver.js”></script>
<script type=”text/javascript” language=”javascript”>
< !–
function init_Scroll(){
// 创建一个垂直滚动对象的实例
// 显示容器为container,你也可以直接写document.getElement('container')
var scrollPics = new scrollVertical('container','message','ul');
scrollPics.speed = 4000;     // 间隔时间,更准确的说,是滚动完一行,停留的时间
scrollPics.isPause = true;   // 是否暂停为true,不能一开始就滚动,需要先停留下,然后再滚动

// 这个则是指定,第一次显示滚动内容第一行停留的时间为2秒
// 2秒后isPause为false,也就不暂停滚动,开始滚动了。
// 这个时间大家可以自己设定
var timer_start = setTimeout(function(){clearTimeout(timer_start);scrollPics.isPause = false;},2000);

Event.addEvent(scrollPics.scrollArea,”mouseover”,function(){scrollPics.isPause = true;});
Event.addEvent(scrollPics.scrollArea,”mouseout”,function(){scrollPics.isPause = false;});
}
/*
* 其实这里也可以直接写init_Scroll();
* 应为我已经把脚本放到文档的最后面,
* 在加载脚本之前,所有的DOM节点已经加载完毕
* 已经可以直接用脚本访问DOM节点了
*/
Event.addEvent(window,'load',init_Scroll);
//–>
</script>
</body>
</html>

刚才给大家介绍了实现这个特效的JavaScript的相关知识点,现在就讲下相关的CSS技巧,还是先看看CSS代码:

#container{
margin:0 auto;
margin-top:10px;
width:720px;
height:100px;
overflow:hidden;
}

首先,一定要设置“overflow:hidden;”,为什么?看看我们把高度设置为了height:100px;,正好是一行信息的高度,如果不设置overflow:hidden,在firefox也许没有问题,它会严格按照你指定的高度显示相应高度的内容,而隐藏多余的部分(多余的5行),而在 IE中,则会自做聪明把容器挤高,让它把里面的全部内容都显示出来。而我们的效果需要的是只显示一行,而隐藏多余的5行。

如果你仔细看过了JavaScript代码,你可能要问了,怎么有多余的5行?其实代码中已经解释了:

// 创建复制内容节点
var copydiv = document.createElement('div');
// 这个地方感觉有点嵌妥
// 直接使用element.id的方式,不过看上去,主流的浏览器都支持
// 标准的DOM Core方法:
// copydiv.setAttribute('id',s_area.id + “_copymsgid”)
copydiv.id = s_area.id + “_copymsgid”;
// 复制原始的信息
// 将原始的信息s_msg中的内容,直接用innerHTML写到
copydiv.innerHTML = s_msg.innerHTML;
// 设置复制信息节点的高度
copydiv.style.height = this.msgHeight + “px”;
// 将复制节点添加到原始接点(scrollMsg)后
// 其实实现的方法就是将复制信息节点(copydiv)添家到显示区域的节点(scrollArea)中
s_area.appendChild(copydiv);

因为我们又复制了一份信息,并添加到要显示滚动信息的容器中了,所以3行变6行了。接下的内容就没有什么好讲了,大家看我的注释,应该可以很清楚了。唯一要注意的一点就是这里

// 滚动高度等于显示滚动区域高度时(滚动完一行,一行内容全部显示)
// 暂停4秒中,然后再开始执行下依次滚动。
if (o.scrollHeight % s_area.offsetHeight == 0) {
o.scrollTimer = setTimeout(anim, o.speed);
}
else {
// 在两行内容之间过度滚动时,每10豪秒上升1px
o.scrollTimer = setTimeout(anim, 10);
}

o.scrollHeight % s_area.offsetHeight == 0,要明白它确切的意思。看看这里的CSS代码:

#container{
margin:0 auto;

margin-top:10px;
width:720px;
height:100px;
overflow:hidden;
}

#message,
#message_copymsgid{
margin:0;
width:720px;
overflow:hidden;
}
#container ul{
float:left;
width:720px;
height:100px;
overflow:hidden;
clear:both;
}

ul也就是我们一行的高度为100px,o.scrollHeight已经滚动的高度。不知道你发现了问题没有?对了,问题就在 % s_area.offsetHeight,我之所以没有更正原程序里的这个缺点,是因为如果你不对#container做任何修饰,这么写没有错。因为s_area也就是#container这里我只定义了height:100px;,如果我要是这么写:

#container{
margin:0 auto;
margin-top:10px;
width:720px;
height:100px;
overflow:hidden;
border:1px;
padding:1px;
}

你觉得会有什么结果?还有o.scrollHeight % s_area.offsetHeight == 0要怎么改该?细心的朋友估计已经看出来了,设置padding和border值后,s_area.offsetHeight的高度就要大雨100px了,这样之后,滚动一次后,就是把下面一行的内容露出一点,滚动几次后,应该说信息全部滚动完后,滚动的距离o.scrollHeight % s_area.offsetHeight == 0就不是0了。就会出现问题。

最后要讲的是position的使用了。其实滚动的动画,就是隔一段时间利用setTimeout函数,改变一下ul的top值。好了,特效讲解完毕,也不知道我这么讲解一个特效,对你有没有帮助。特效里面的ceateElement,appendChild等等DOM的方法,大家还是需要不断的学习,可能才能完全明白和掌握,我这里不可能一一都讲清楚。

声明:本文采用BY-NC-SA协议进行授权。转载请注明转自:JavaScript DOM编程实例教程 – 垂直无缝滚动图片

« »

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(Spamcheck Enabled)