js实现消息滚动显示效果
最近的项目中需要实现一个滚动消息的功能,以前在网上也看到了些,但是都不太符合我的需求,于是就自己写了个,效果:
缓动类型:
下面来说说程序的实现思路,最初我设想的是通过改变div的left属性来实现位移的效果,但是在实际操作中发现有一些困难,首先是当消息条目一旦多起来,需要移动的元素就太多了,会造成性能问题,其次每个条目也可能大小不一致,这样还得再程序中修正大小。所以后来我换了一种思路,把滚动消息放在一个表格内,这样只需要移动这个表格就可以了,思路有了,结下了来实现就简单了,程序的构造器接受3个参数,container, num, options,分别是消息包装器,滚动条目,配置选项。
配置选项有一下一些参数
-
var ScrollMsg = Class.create({
-
options : {
-
vertical : false,//是否垂直,需要在html代码中提供多行的表格
-
auto : true,//是否自动
-
duration : 1000,//滚动持续时间
-
fps : 100,//帧数
-
pauseTime : 1000,//停顿时间间隔
-
onStart : function(){},//滚动开始前执行
-
onFinish : function(){},//滚动结束后执行
-
fx : function(t,b,c,d){//缓动算法
-
return c*(t/=d)*t*t*t + b;
-
}
-
}
-
});
这些参数都是可选的,需要注意的是如果auto设置为了false,那么pauseTime将无效。
接下来是程序的主要函数,它接受一个可选的index参数用于指定移到哪一个位置,如果不指定则默认为当前的下一个位置
-
moveto : function (index){
-
if(typeof index == "undefined" || index < 0){
-
index = (this.currIndex < this.count-1 ? this.currIndex+1 : 0);
-
} else{
-
index = (index >= this.count ? this.count-1 : index);
-
}
-
this.clear();
-
this.from = parseInt(this.containerStyle[this.pos]);
-
this.change = -index * this.distance - this.from;
-
this.currIndex = index;
-
this.ctime = new Date().getTime();
-
window.setTimeout(this.options.onStart.Bind(this), 10);
-
this.timer = window.setInterval((function (){
-
this.step();
-
}).Bind(this), Math.round(1000/this.options.fps));
-
}
移动开始的时候会首先校正index参数,然后清空当前的timer,防止当有多个interval的时候出现位移混乱,然后记录消息包装器的当前位置,这个数值是通过ScrollMs对象的containerStyle属性得到的,containerStyle属性指向消息包装器的ComputedStyle
-
var currentStyle = function (element){
-
element = $(element);
-
return element.currentStyle || document.defaultView.getComputedStyle(element, null);
-
};
-
this.containerStyle = currentStyle(this.container);
这样当以后需要的时候就不需要重复取了,直接.属性名即可。
-
this.change = -index * this.distance - this.from;
是说移动对象相对于当前位置要改变的量,这个可以用当前index*平均位移量再减去当前位置得到。this.ctime = new Date().getTime();是记录移动的开始时间,有了这些基本参数就可以调用缓动函数了。
程序还有一个start和stop函数用于启动停止滚动。如果auto为false则调用start函数只能移动一次。
step函数是用来进行逐步移位的,它首先判断当前时间是否小于等于位移的终止时间,如果为true则位移一次,否则就清空timer,然后把移动对象的位置设置为最终值
-
this.container.style[this.pos] = this.from + this.change;
这主要是考虑有可能上次位移没有达到最终值,这次位移的时间恰好又超出了终止时间从而造成位置偏差而进行的校正。
整个程序结构基本就是这样,通过修改还可以做成图片轮训展示的效果
源代码:
-
Function.prototype.Bind = function (object){
-
var args = Array.prototype.slice.call(arguments, 1);
-
var fun = this;
-
return function() {
-
return fun.apply(object, args.concat(Array.prototype.slice.apply(arguments)));
-
}
-
};
-
Array.prototype.each = function(fun){
-
var b = true;
-
for (var i = 0, len = this.length; i < len; i++) { !!fun.call(this, this[i], i) ? b : (b = false); }
-
return b;
-
};
-
window.$ = function (id){
-
if(arguments.length > 1){
-
var els = [];
-
Array.prototype.each.call(arguments, function (a){els.push($(a));});
-
return els;
-
} else if("string" == typeof id){
-
return document.getElementById(id);
-
} else if(id.length && id.join){
-
return $.apply(null, id);
-
} else {
-
return id;
-
}
-
};
-
var Class = {
-
create: function (def) {
-
return function (){Extend(this, def||{}); def = null; this.initialize.apply(this, arguments); }
-
}
-
};
-
var Extend = function(destination, source) {
-
for (var property in source) { destination[property] = source[property]; }
-
return destination;
-
};
-
var currentStyle = function(element){
-
element = $(element);
-
return element.currentStyle || document.defaultView.getComputedStyle(element, null);
-
};
-
var ScrollMsg = Class.create({
-
options : {
-
vertical : false,
-
auto : true,
-
duration : 1000,
-
fps : 100,
-
pauseTime : 1000,
-
onStart : function(){},
-
onFinish : function(){},
-
fx : function (t,b,c,d){
-
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
-
}
-
}
-
});
-
ScrollMsg.prototype = {
-
initialize : function (container, num, options){
-
this.setOptions(options);
-
this.container = $(container);
-
this.containerStyle = currentStyle(this.container);
-
this.count = num;
-
this.distance = (this.options.vertical ? this.container.scrollHeight : this.container.scrollWidth) / num;
-
this.currIndex = 0;
-
this.timer = null;
-
this.pauseTimer = null;
-
this.pos = this.options.vertical ? "top" : "left";
-
if(this.options.auto){
-
this.start();
-
}
-
},
-
setOptions : function (options){
-
Extend(this.options, options||{});
-
},
-
moveto : function (index){
-
if(typeof index == "undefined" || index < 0){
-
index = (this.currIndex < this.count-1 ? this.currIndex+1 : 0);
-
} else{
-
index = (index >= this.count ? this.count-1 : index);
-
}
-
this.clear();
-
this.from = parseInt(this.containerStyle[this.pos]);
-
this.change = -index * this.distance - this.from;
-
this.currIndex = index;
-
this.ctime = new Date().getTime();
-
window.setTimeout(this.options.onStart.Bind(this), 10);
-
this.timer = window.setInterval((function (){
-
this.step();
-
}).Bind(this), Math.round(1000/this.options.fps));
-
},
-
step : function (){
-
var time = new Date().getTime();
-
if(time <= this.ctime + this.options.duration){
-
var t = time - this.ctime;
-
this.container.style[this.pos] = this.compute(t);
-
}else{
-
this.clear();
-
this.container.style[this.pos] = this.from + this.change;
-
window.setTimeout(this.options.onFinish.Bind(this), 10);
-
if(this.pauseTimer && this.options.auto)this.start();
-
}
-
},
-
clear : function (){
-
window.clearInterval(this.timer);
-
},
-
compute : function (t){
-
return Math.round(this.options.fx(t, this.from, this.change, this.options.duration)) + "px";
-
},
-
stop : function (){
-
window.clearTimeout(this.pauseTimer);
-
this.pauseTimer = null;
-
},
-
start : function(){
-
window.clearTimeout(this.pauseTimer);
-
this.pauseTimer = window.setTimeout((function (){
-
this.moveto();
-
}).Bind(this), this.options.pauseTime);
-
}
-
};