JS 动画初探(下)
上一章我们讲在元素移动到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目标量为主。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据