正在加载中

最后更新于 2018年12月21日

预览图:

之前在登录框弹窗时我们做了一个遮罩层,当时的遮罩层是通过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;
}

以上,修缮完成。

  • weixiao kaixin tushetou jingkong deyi fanu liezui liuhan daku ganga bishi nanguo lihai qian yiwen numu tu yi haixiu se fadai minyan hehe henkaixin huaji biyiyan kuanghan maimeng shui xiaku penqi zhangzui pen aini ye niu laji ok chigua renshi kongbu shuai xiaoxiese touxiao huaixiao jingnu chihuai kaisang xiaoku koubi zhuangbi lianhong kanbujian shafa zhijing xiangjiao dabian yaowan redjing lazhu rizhi duocang chixigua hejiu xixi xiaopen goukun xiaobuchu shenme wusuowei guancha lajing chouyan xiaochi bie zhadanzui zhadanxiao