JS 动画初探(下)

78 0

上一章我们讲在元素移动到end位置的时候,会有两个动作同时产生,一个是正常的移动element.style[attr] = parseInt(getStyle(element,attr)) + step + 'px';一个是if判断如果当前的位置已经大于或者等于end的时候,element.style[attr] = end + 'px';这样可以解决动画的最后一些的突兀感,不会先超出,然后再移动回来,而是在同一个时间完成,但是这样并不是很好的解决办法。

实际上你移动距离还是超出了,只不过后面又回来了,这样可能对以后js代码有影响,所以这里就再次调整。

解决超出返回的回弹问题

var timer = setInterval(function(){
            if(step > 0 && Math.abs(parseInt(getStyle(element,attr)) - end) <= step) {
                element.style[attr] = end + 'px';
                clearInterval(timer);
            }else if(step < 0 && (parseInt(getStyle(element,attr)) - end) <= Math.abs(step)) {
                element.style[attr] = end + 'px';
                clearInterval(timer);
            }else {
                element.style[attr] = parseInt(getStyle(element,attr)) + step + 'px';
            }
        },time)

这里普通状态的 移动在else中运行,当元素当前的位置和end的值相减得到还需要移动的距离,如果这个距离小于或者等于每次step步进的值,那么久直接让他的位置等于end的位置,这样完美的解决了超过然后返回来的问题。

当元素的位置减去end的值,如果是往左得到的是负值,所以要用Math.abs()来转换成绝对值,也就是正值去比对,如果是往右,在元素不可能超出屏幕外的前提下,也就是说元素最大的移动位置值是0,那么0 - end的值,end往左是-xxx的负值,负负得正,这里得到的是正值,不用转换,但是步进step是负值,前面if判断的时候写的,所以要对step转换。

即便0-end的值是负值,

下面我们再来优化一下设置移动方向的问题,我们之前用的是top,left这两个值,但是很容易让人误解,以为right和bottom也能使用,但实际上只有top和left这两个选择,那么我们可以改用x和y这两个坐标名来传参,那么就要做个判断了。

优化移动方向传参

var attr = obj['attr'] == 'x' ? 'left' : obj['attr'] == 'y' ? 'top' : 'left';

把attr的判断改一下,如果是x就返回left,如果是y则返回top,默认是left。

缓冲远动

元素先快然后越来越慢,最后停止。

//设置动画
$('#pox').click(function() {
    $('#box').animation({
        'attr': 'x',
        'step': 7,
        'start': 300,
        'end': -500,
        'type': 1,
        'speed': 6
    })
});

//设置动画
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' : 'left';
        var step = obj['step'] != undefined ? obj['step'] : 10;
        var start = obj['start'] != undefined ? obj['start'] : parseInt(getStyle(element, attr));
        var time = obj['time'] != undefined ? obj['time'] : 50;
        var end = start + obj['end'];
        var type = obj['type'] == 0 ? 'constant' : obj['type'] == 1 ? 'buffer' : 'buffer';
        var speed = obj['speed'] != undefined ? obj['speed'] : 6;
        if (start > end) step = -step;
        element.style[attr] = start + 'px';
        clearInterval(window.timer);
        timer = setInterval(function() {

            if (type == 'buffer') {
                step = (end - parseInt(getStyle(element, attr))) / speed;
                step = (step > 0) ? Math.ceil(step) : Math.floor(step);
            }
            if (step > 0 && Math.abs(parseInt(getStyle(element, attr)) - end) <= step) {
                element.style[attr] = end + 'px';
                clearInterval(timer);
            } else if (step < 0 && (parseInt(getStyle(element, attr)) - end) <= Math.abs(step)) {
                element.style[attr] = end + 'px';
                clearInterval(timer);
            } else {
                element.style[attr] = parseInt(getStyle(element, attr)) + step + 'px';
            }
        }, time)
    }
    return this;
}

这里我们新增加了两个值,一个是type,表示是否缓动,一个是speed表示倍速。

然后if判断,如果是是obj[‘attr’]是1,那么type等于缓动buffer,然后再判断是否设置了倍速,这里我们设置了6。

为了实现缓动效果,那么每次步进的值都不一样,所以要在间歇调用的函数开头就要对step做改动。

if判断如果type是buffer,那么让step等于(end - 当前元素位置)除以speed。

然后step在往右时得到的是正值,但是会有小数点,我们要做舍入操作,Math中有一个ceil()方法,它不管小数点后面的数字多大,只要有便会进1。

step往左的时候,得到的是负值,负值使用ceil()无法进1,反倒是会舍弃小数点后面的数,这里就要使用floor()方法,floor()方法在负数的时候只要小数点后面有值,就会进1。

ceil()和floor()的区别

ceil在值是正值的时候,小数点后面有值便会进1,负值的时候舍去小数点后面的值。floor则是相反,正值的时候舍去小数点后面的值,负值的时候小数点后面有值便会进1。

为什么要使用这两个方法,是因为当元素移动的距离与end值越来越进的时候,step得到的值会越来越小,如果我们不让他后面的值都是一样的话,就没有缓动的效果了,他会是一个加速状态,如果使用了这两个方法,那么最后的那段距离会每次以1px的值进行移动,应为0.xx的时候这两个方法都进1了,这样最后就会达到想要的效果。

计算的过程:

end = -500;start = 300;speed = 6;type = 1;传入的值

end = start + obj[‘end’] = 300 - 500 = -200;

step = (-200 - 300)/6 = -83.3333333.... ≈ -84;

第一次移动300-84 = 216,然后这样无限重复,然后到了元素的位置是-198的时候

step = (-200 + 198)/6 = -0.3333.... ≈ -1;

if判断stpe<0;并且-198 -(-200) <= -1;判断条件不成立,于是位置移动1px,直到位置到了end的位置-200的时候,step=0,if判断的时候stpe<0;-200 - (-200) <= 0;条件成立,element.style[attr] = end + 'px';然后清除间歇调用。

简单点来说,假设我们移动的位置是正值,移动100px,那么end = 当前位置300 + 100 = 400,然后step不管怎么变,他永远是最终位置end 减去当前位置其中的一段距离而已(需要移动的距离中的一段),那么不断的移动下去,总能移动到end上,然后我们用舍入的方法让他最后的时候移动距离都是+1,最后移动到400的时候,需要移动的距离是0了,那么stpe也等于0,那么移动完毕,因为舍入的关系,移动的最小距离就只能是1了,再加上每次舍入都是整数,所以实际上Math.abs(parseInt(getStyle(element,attr)) - end) <= step这段不存在小于的,只有等于,但是以防万一加了小于。

如果说移动的位置是负值,移动到-500的位置,那么end得到的是300 + (-500) = -200; 最终的位置是-200,但是实际要移动的位置要移动800才对,这个800在step中就已经算了,就是end-元素当前位置,得到的是总共要移动位置,剩下的就都一样了。

attr中传入w和h来改变元素的宽度和高度

我们通过parseInt(getStyle(element,attr))可以返回当前元素的对应的值,那么宽高这些自然也可以获取到。

//设置动画
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' : 'left';
        var step = obj['step'] != undefined ? obj['step'] : 10;
        var start = obj['start'] != undefined ? obj['start'] : parseInt(getStyle(element,attr));        
        var time = obj['time'] != undefined ? obj['time'] : 50;
        var end = start + obj['end'];
        var type = obj['type'] == 0 ? 'constant' : obj['type'] == 1 ? 'buffer' : 'buffer';
        var speed = obj['speed'] != undefined ? obj['speed'] : 6;
        if(start > end) step = -step;
        element.style[attr] = start + 'px';
        clearInterval(window.timer);
        timer = setInterval(function(){
            
            if(type == 'buffer') {
                step = (end - parseInt(getStyle(element,attr))) / speed;
                step = (step > 0) ? Math.ceil(step) : Math.floor(step);
            }
            document.getElementById('dox').innerHTML += getStyle(element, attr) + '<br/>';
            if(step > 0 && Math.abs(parseInt(getStyle(element,attr)) - end) <= step) {
                element.style[attr] = end + 'px';
                clearInterval(timer);
            }else if(step < 0 && (parseInt(getStyle(element,attr)) - end) <= Math.abs(step)) {
                element.style[attr] = end + 'px';
                clearInterval(timer);
            }else {
                element.style[attr] = parseInt(getStyle(element,attr)) + step + 'px';
            }
        },time)
    }
    return this;
}

设置既可以使用增加量,也可以使用目标量。

增加量表示需要增加多少,目标量表示到这个位置。

那么就要做个判断了。

$('#pox').click(function(){
    $('#box').animation({
        'attr' : 'h',
        'step' : 7,
        'end' : 300,
        'add' : 50,
        'type' : 1,
        'speed' : 6
    })
});

//设置动画
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' : 'left';
        var step = obj['step'] != undefined ? obj['step'] : 10;
        var start = obj['start'] != undefined ? obj['start'] : parseInt(getStyle(element,attr));        
        var time = obj['time'] != undefined ? obj['time'] : 50;
        var end = obj['end'];
        var add = obj['add'];
        if(add != undefined && end == undefined) {
            end = add + start;
        }else if(add == undefined && end == undefined) {
            throw new Error('end目标量或者add增加量没有值!');
        }
        var type = obj['type'] == 0 ? 'constant' : obj['type'] == 1 ? 'buffer' : 'buffer';
        var speed = obj['speed'] != undefined ? obj['speed'] : 6;
        if(start > end) step = -step;
        element.style[attr] = start + 'px';
        clearInterval(window.timer);
        timer = setInterval(function(){
            
            if(type == 'buffer') {
                step = (end - parseInt(getStyle(element,attr))) / speed;
                step = (step > 0) ? Math.ceil(step) : Math.floor(step);
            }
            document.getElementById('dox').innerHTML += getStyle(element, attr) + '<br/>';
            if(step > 0 && Math.abs(parseInt(getStyle(element,attr)) - end) <= step) {
                element.style[attr] = end + 'px';
                clearInterval(timer);
            }else if(step < 0 && (parseInt(getStyle(element,attr)) - end) <= Math.abs(step)) {
                element.style[attr] = end + 'px';
                clearInterval(timer);
            }else {
                element.style[attr] = parseInt(getStyle(element,attr)) + step + 'px';
            }
        },time)
    }
    return this;
}

if判断,如果增量add存在,目标量end不存在,那么end = add + start(元素本身大小),如果都不存在,那么抛出错误,如果两个都存在,if判断的时候会直接跳过了,因为条件都不对,然后以end目标量为主。

0
  • 本文分类:JavaScript
  • 本文标签:ceilfloorthrow
  • 流行热度:已超过 78 人围观了本文
  • 发布日期:2018年12月14日 - 0时42分00秒
  • 版权申明:本文系作者@木灵鱼儿原创发布在木灵鱼儿站点。未经许可,禁止转载。

评论(0)

微信收款码
微信收款码