首先,我们需要先封装一个css方法,用以获取元素的样式。
//elem 元素 attr 元素的属性名
function css(elem,attr){
return elem.currentStyle ? elem.currentStyle[attr] : window.getComputedStyle(elem)[attr];
}
然后,我们想要一个元素的width动起来,怎么做?给它一个定时器,每隔一段时间改变它的width值,到了想要的值时就清除定时器。如果还想改变别的样式值呢?那么就给这个定时器封装成函数方法,把里面要改变的变成变量,需要用的时候,调用它,传入想改变的样式名就行了。
此时就有了这样的方法:
//elem 元素 attr 属性名 value 属性的改变值
function animate(elem,attr,value){
var timer = setInterval(function(){
//设置变量start存储元素要改变的样式的初值
var start = parseInt( css(elem,attr) ),
//设置变量speed样式改变的速度
var speed = (value-start)/10;
//当speed负值或者正值时需要给它取整,以免出现start永远不能等于value的情况
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
start += speed;
if(start == value){
clearInterval(timer);
}else{
elem.style[attr] = start + 'px';
}
},13)
}
但是,这样的方法不能实现opacity的改变
那么就有了这样的方法:
function animate(elem,attr,value){
var timer = setInterval(function(){
//设置变量end存储元素要改变的样式的最终值
var start = 0, end = 0;
if(attr == 'opacity'){
//Math.round防止无限小数兼容ie
start = Math.round( parseFloat( css(elem,attr) )*100 );
end = value*100;
}else{
start = parseInt( css(elem,attr) );
end = value;
}
var speed = (end-start)/10;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
start += speed;
if(start == end){
clearInterval(timer);
}else{
if(attr == 'opacity'){
elem.style.opacity = start/100;
//兼容IE
elem.style.filter='alpha(opacity='+start+')';
}else{
elem.style[attr] = start + 'px';
}
}
},13)
}
这样的方法,只是在元素本身被设置过属性情况下,那如果元素没有设置这个你想要改变的属性呢?
我们先来看下,当元素没有设置属性值的时候,CSS方法获取的是什么。
比如,获取一个left,css(div,'left'),有的浏览器会返回0px,但是有的浏览器会返回一个undefined或者auto或者其他他非数字的值;同样,关于opacity这个属性,其他浏览器会返回1,但是IE浏览器会返回一个undefined。
一般HTML+CSS布局的时候,大家通常会这样写透明度:filter:alpha(opacity=100);opacity:1;
因为IE浏览器的透明度是filter:alpha(opacity=100);这样的写法就是为了兼容IE的。
知道了原理,那么,这个animate就可以这样写了:
function animate(elem,attr,value){
var timer = setInterval(function(){
var start = 0, end = 0;
if(attr == 'opacity'){
//兼容IE 当没有设置fliter时,默认100;
if(!css(elem,attr)){
start = 100;
}else{
start = Math.round( parseFloat( css(elem,attr) )*100 );
}
end = value*100;
}else{
//兼容ie 没有设置attr时,默认0(不加parseInt,IE会报错)
if(!parseInt(css(elem,attr))){
start = 0;
}else{
start = parseInt( css(elem,attr) );
}
end = value;
}
var speed = (end-start)/10;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
start += speed;
if(start == end){
clearInterval(timer);
}else{
if(attr == 'opacity'){
elem.style.opacity = start/100;
//兼容IE
elem.style.filter='alpha(opacity='+start+')';
}else{
elem.style[attr] = start + 'px';
}
}
},13)
}
好了,元素单个的属性运动方法已经完毕。
那想要多个属性同时运动呢?
我们可以把attr和对应的value用对象存起来,比如这样 {width:100,height:100,opacity:1}。然后遍历这个对象,依次改变对象里的属性对应的值,也就是改变了元素的属性值。对象遍历我们用for in 方法。
比如,
var json = {width:100,height:100,opacity:1};
for(var i in json){
console.log(i)
}
你会发现得到的是width,height,opacity这些字符串。那么json[i]就代表了json对象里的属性值。
还有,前面的方法,在实际运用中会出现BUG。因为可能会出现,上一次运动还没完,用户又开始运动,即开了多个定时器的情况。这样就会出现问题。
通常做法是在开始运动前就清除定时器,这样就不会出现问题。
我们的前面的方法里直接在定时器前面写clearInterval(timer)是不行的,会出错 timer is not defined 。
那么我们可以这样,把元素看成一个对象,把timer设置成元素的一个属性 ,即elem.timer。
所以,
function animate(elem,json){
clearInterval(elem.timer);
elem.timer = setInterval(function(){
//遍历json,依次改变每个属性的属性值
for(var attr in json){
var start = 0, end = 0;
if(attr == 'opacity'){
//兼容IE 当没有设置fliter时,默认100;
if( !css(elem,attr) ){
start = 100;
}else{
//Math.round防止出现无限小数
start = Math.round( parseFloat( css(elem,attr) )*100 );
}
end = parseFloat(json[attr])*100;
}else{
//兼容ie 没有设置attr时,默认0(不加parseInt,IE会报错)
if(!parseInt(css(elem,attr))){
start = 0;
}else{
start = parseInt( css(elem,attr) );
}
end = parseInt(json[attr]);
}
var speed = (end-start)/10;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
start += speed;
if(start == end){
clearInterval(elem.timer);
}else{
if(attr == 'opacity'){
elem.style.opacity = start/100;
//兼容IE
elem.style.filter='alpha(opacity='+start+')';
}else{
elem.style[attr] = start + 'px';
}
}
},13)
}
这样貌似写完了,其实还没完,这样写会有BUG,会出现只实现了第一个属性的运动,而其他属性的运动没有到达你想要的最终值。因为方法里是这样判定的,当start == end的时候,判定运动终止,但是,现在不止有一个start和end,第一个属性的start==end完成了,但是后面的属性的start==end并没有完成。
所以,我们可以用布尔值来做判定条件。
function animate(elem,json){
clearInterval(elem.timer);
elem.timer = setInterval(function(){
//运动终止条件
var flag = true;
//遍历json,依次改变每个属性的属性值
for(var attr in json){
var start = 0, end = 0;
if(attr == 'opacity'){
//兼容IE 当没有设置fliter时,默认100;
if( !css(elem,attr) ){
start = 100;
}else{
//Math.round防止出现无限小数
start = Math.round( parseFloat( css(elem,attr) )*100 );
}
end = parseFloat(json[attr])*100;
}else{
//兼容ie 没有设置attr时,默认0(不加parseInt,IE会报错)
if(!parseInt(css(elem,attr))){
start = 0;
}else{
start = parseInt( css(elem,attr) );
}
end = parseInt(json[attr]);
}
var speed = (end-start)/10;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
start += speed;
//当start != end时,flag = false;,运动继续
if(start != end){
flag = false;
}
if(attr == 'opacity'){
elem.style.opacity = start/100;
//兼容IE
elem.style.filter='alpha(opacity='+start+')';
}else{
elem.style[attr] = start + 'px';
}
}
//当所有的start == end时,flag = true;,运动停止
if(flag){
clearInterval(elem.timer);
}
},13)
}
如果想把animate做成链式运动方法,很简单,只要多加个参数fn,在最后判定运动完成时回调函数就行。改变运动的快慢也很简单,只要再加个参数time。
所以最终版:
function animate(elem,json,time,fn){
clearInterval(elem.timer);
elem.timer = setInterval(function(){
//运动终止条件
var flag = true;
//遍历json,依次改变每个属性的属性值
for(var attr in json){
var start = 0, end = 0;
if(attr == 'opacity'){
//兼容IE 当没有设置fliter时,默认100;
if( !css(elem,attr) ){
start = 100;
}else{
//Math.round防止出现无限小数
start = Math.round( parseFloat( css(elem,attr) )*100 );
}
end = parseFloat(json[attr])*100;
}else{
//兼容ie 没有设置attr时,默认0
if( !parseInt(css(elem,attr)) ){
start = 0;
}else{
//parseInt防止json对象里属性值带px
start = parseInt( css(elem,attr) );
}
end = parseInt(json[attr]);
}
//如果有time实参传入,就执行speed = (end-start)/ time ,否则speed = (end-start)/10;
var speed = (end-start)/( time || 10 );
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
start += speed;
//当start != end时,flag = false;,运动继续
if(start != end){
flag = false;
}
if(attr == 'opacity'){
elem.style.opacity = start/100;
//兼容IE
elem.style.filter='alpha(opacity='+start+')';
}else{
elem.style[attr] = start + 'px';
}
}
//当所有的start == end时,flag = true;,运动停止
if(flag){
clearInterval(elem.timer);
//运动终止时,如果有函数实参传入就执行回调函数
fn & fn();
}
},13)
}
本文均为荣益互联摘自权威资料,书籍,文章,或来自网络,如有版权纠纷或违规问题,请联系我们删除。我们欢迎您的分享,谢绝直接抄袭复制。感谢…