JS 增强弹窗 队列动画
预览图:
之前在登录框弹窗时我们做了一个遮罩层,当时的遮罩层是通过js创建了,但是如果要加上动画效果,那么你创建的话就十分不方便,所以这里直接在html上建了一个div元素,然后通过js设置css。
HTML部分:
<div class="">
<div id="login_masked"></div> //这里创建一个div元素用于遮罩层
<div id="login">
<i class="login_off"><span class="off">+</span></i>
<h2 id="login_title">网站登录</h2>
<form action="" id="login_form">
<div class="user">账 号:<input type="text" name="user" class="text" /></div>
<div class="pass">密 码:<input type="password" name="pass" class="text" /></div>
<div class="buttom"><input type="button" name="submit" value="登录" class="submit" /></div>
<div class="other">注册新用户 | 忘记密码?</div>
</form>
</div>
</div>
CSS部分:
#login_masked {
position: absolute;
top: 0;
left: 0;
display: none;
background-color: #000;
opacity: 0;
filter: alpha(opacity=0);
}
遮罩层自然是要控制大小的,并且不会占据空间,这里使用absolute,但是绝对定位他有一个位置跟随性,如果你没有设置他的top和left的位置,那么默认是在原来的位置上的,所以这里top:0;left:0;做一个位置重置。
默认遮罩层是不显示的,这里就设置display: none; 透明度为0;
当用户点击后遮罩层弹出,宽度和高度为可视宽高,display:block;透明度为0.3
JS部分:
封装库
//设置遮罩层
Base.prototype.Masked = function() {
for(var i = 0;i<this.arr.length;i++) {
this.arr[i].style.width = getInner().width + 'px';
this.arr[i].style.height = getInner().height + 'px';
addEvent(window,'scroll',scrollTop);
}
return this;
}
//删除遮罩层
Base.prototype.removeMasked = function() {
for(var i = 0;i<this.arr.length;i++) {
this.arr[i].style.display = 'none'
removeEvent(window,'scroll',scrollTop);
}
return this;
}
调用部分
var login = $('#login');
var masked = $('#login_masked');
$('#header .nav-login').hover(function(){
$(this).css('background-color','rgba(0,0,0,.3)');
},function() {
$(this).css('background-color','rgba(0,0,0,0)');
});
$('#header .nav-login').click(function(){
login.css('clip','auto').css('opacity','1').center().resize(function(){
if(login.css('opacity') == '1'){
masked.Masked();
};
});
masked.css('display','block').Masked().animation({
'attr' : 'o',
'end' : 30,
'step' : 7,
});
$('html').css('overflow','hidden');
});
$('#login .login_off').click(function(){
login.css('clip','rect(0 0 0 0)').css('opacity','0');
masked.animation({
'attr' : 'o',
'end' : 0,
'step' : 7,
'fn' : function(){
masked.removeMasked();
}
});
$('html').css('overflow','auto');
});
调用的时候,我们使用了新的方法,就是动画队列,当一个动画完成后触发另一个动画。
其原理就是在传入的obj对象中最后写了一个fn的属性,然后他赋予一个function,这个function里面写好下一个需要触发的动画,这里我们在login_off元素,关闭按钮上使用了这个方法,当用户点击关闭按钮后,先是遮罩层从不透明转为透明,然后再触发删除遮罩层效果。
如果说你想要使用this对象,那么直接在fn的function中写是无效的,此时的这个this指向的是fn的作用域,并不是你点击的对象。
$('#login .login_off').click(function(){
login.css('clip','rect(0 0 0 0)').css('opacity','0');
masked.animation({
'attr' : 'o',
'end' : 0,
'step' : 7,
'fn' : function(){
alert(this); // this不是指向login_off元素,而是fn
}
});
$('html').css('overflow','auto');
});
让this指向login_off也很简单,在click的function中创建一个变量,并等于this,此时这个变量就是点击的元素了
$('#login .login_off').click(function(){
var _this = this;
login.css('clip','rect(0 0 0 0)').css('opacity','0');
masked.animation({
'attr' : 'o',
'end' : 0,
'step' : 7,
'fn' : function(){
alert(_this); // _this指向login_off
}
});
$('html').css('overflow','auto');
});
我们使用的队列方法要在动画的封装库中修改一下。
我们在超时调用timer的里面if判断是否已经达到要求,然后运行对应的 setOpacity();和 setAttr();方法,这两个方法运行说明已经达到要求了,那么这个动画已经结束,那么我们只需要在这两个函数中最后面添加运行obj传入的fn的方法,不就可以达到运行完一个动画再运行下一个动画的效果了 。
于是
function setAttr() {
element.style[attr] = end + 'px';
clearInterval(element.timer);
if (obj.fn != undefined) obj.fn();
}
function setOpacity() {
element.style.opacity = parseInt(end) / 100;
element.style.filter = 'alpha(opacity=' + parseInt(end) + ')';
clearInterval(element.timer);
if (obj.fn != undefined) obj.fn();
}
防止错误做一个if判断,fn存在就运行fn的方法。
以上设置好后,当我们点击登录时,遮罩层会有动画效果,点击关闭按钮,遮罩层会先慢慢透明,最后再被display:none;
即便如此,但是还有一个bug需要调整,就是我们不管调用动画的元素有几个,他们都是共用一个timer超时调用,也就是window.timer;这样就会产生一个问题,当你快速触发多个动画效果的元素时,元素动画没有完成就被清理掉了timer然后又触发下一个元素动画,这样就会导致前面的元素就好像卡住了一样,但是你单个调用时便不会有这个问题。
修复共用一个timer
下面我们来整理下思绪。
首先就是触发的事件,我们的事件都是在元素上触发的,以hover()方法中的mouseover和mouseout事件来讲,当鼠标移入这个元素时,触发mouseover事件,这个事件调用animation动画,动画里面创建window.timer超时调用。当你在这个元素里面不断移动时,触发的元素永远是同一个。
那么我们创建window.timer的原因是因为事件会重复调用animation动画,然后不断创建timer超时调用,所以只能创建一个可以被找到的timer,也就是window.timer,然后在第二次调用时清理掉之前的。
既然是要一个可以被找到的timer,而每次触发的元素都是同一个,那直接创建一个这个元素的timer。
于是
element.timer 即可,这个超时调用既可以在重复的时候被找到,在不同的元素触发的时候也不会清理到之前的,因为element发生了变化。
//设置动画
Base.prototype.animation = function(obj) {
for (var i = 0; i < this.arr.length; i++) {
var element = this.arr[i];
var attr = obj['attr'] == 'x' ? 'left' : obj['attr'] == 'y' ? 'top' :
obj['attr'] == 'w' ? 'width' : obj['attr'] == 'h' ? 'height' :
obj['attr'] == 'o' ? 'opacity' : 'left';
var start = obj['start'] != undefined ? obj['start'] :
attr == 'opacity' ? getOpacity() :
parseInt(getStyle(element, attr));
var step = obj['step'] != undefined ? obj['step'] : 20;
var time = obj['time'] != undefined ? obj['time'] : 30;
var add = obj['add'];
var end = obj['end'];
var type = obj['type'] == 0 ? 'constant' : obj['type'] == 1 ? 'buffer' : 'buffer';
var speed = obj['speed'] != undefined ? obj['speed'] : 6;
if (add != undefined && end == undefined) {
end = add + start;
} else if (add == undefined && end == undefined) {
throw new Error('alter增量或target目标量必须传一个!');
}
if (start > end) step = -step;
if (attr == 'opacity') {
element.style.opacity = start / 100;
element.style.filter = 'alpha(opacity=' + start + ')';
} else {
element.style[attr] = start + 'px';
}
clearInterval(element.timer); //创建element下的timer
element.timer = setInterval(function() {
if (type == 'buffer') {
step = attr == 'opacity' ? (end - getOpacity()) / speed :
(end - parseInt(getStyle(element, attr))) / speed;
step = (step > 0) ? Math.ceil(step) : Math.floor(step);
}
//document.getElementById('dox').innerHTML += getStyle(element, attr) + 'px' + '<br/>';
if (attr == 'opacity') {
if (step == 0) {
setOpacity();
} else if (step > 0 && Math.abs(getOpacity() - end) <= step) {
setOpacity();
} else if (step < 0 && (getOpacity() - end) <= Math.abs(step)) {
setOpacity();
} else {
var temp = getOpacity();
element.style.opacity = (temp + step) / 100;
element.style.filter = 'alpha(opacity=' + (temp + step) + ')'
}
} else {
if (step == 0) {
setAttr();
} else if (step > 0 && Math.abs(parseInt(getStyle(element, attr)) - end) <= step) {
setAttr();
} else if (step < 0 && (parseInt(getStyle(element, attr)) - end) <= Math.abs(step)) {
setAttr();
} else {
element.style[attr] = parseInt(getStyle(element, attr)) + step + 'px';
}
};
}, time);
function setAttr() {
element.style[attr] = end + 'px';
clearInterval(element.timer); //达成目标清理对应的timer
if (obj.fn != undefined) obj.fn();
}
function setOpacity() {
element.style.opacity = parseInt(end) / 100;
element.style.filter = 'alpha(opacity=' + parseInt(end) + ')';
clearInterval(element.timer); //达成目标清理对应的timer
if (obj.fn != undefined) obj.fn();
}
function getOpacity() {
if (system.ie <= 8) {
var op = getStyle(element, 'filter');
var s = [];
if (op.match(/opacity=([\d]+)/)) {
s = op.match(/\=([\d]+)/);
} else {
throw new Error('ie8一下没有获取到filter中的opacity值');
}
return parseInt(s[1]);
} else {
return parseFloat(getStyle(element, 'opacity')) * 100;
}
}
}
return this;
}
以上,修缮完成。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据