animation

动画优化问题

  • [ ] 优化一:按固定步长运动:边界问题:step不一定是整数,不能正好到达目标。

    • 【解决】加步长判断边界
  • [ ] 优化二:setTimeout递归不传参

    • 【解决】每次执行前首先把清除上次的定时器
  • [ ] 优化三:setTimeout递归传参作用域累积问题优化**

    • 【解决】:里面创建一个函数_move()不传参,在里面让setTimeout调用小函数_move()
  • [ ] 优化四:当前元素,在同一时间只运行一个动画(绑定多个动画事件时)

    • 【解决】共享的定时器放到当前元素的自定义属性上,例如:box.timer

单方向运动

  • [x] 指定时间(setInterval)
    • [ ] 等间隔,按步长运动
      • 注意:运动元素必须设置position,并且制定left的初始值
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        var box = utils.getElementsByClass('box')[0];
        console.log(box)
        var maxLeft = utils.getsetAttr('clientWidth') - box.offsetWidth;
        console.log(box.offsetWidth)
        var duration = 1000;
        var interval = 10;
        var speed = maxLeft / duration * interval;
        var timer = window.setInterval(function() {
        var curLeft = utils.getCss(box,"left");
        if (curLeft>= maxLeft) {
        window.clearInterval(timer);
        utils.setCss(box, 'left', maxLeft);
        return;
        }
        curLeft += speed;
        utils.setCss(box, "left", curLeft);
        }, interval);
- [ ]   **等间隔,按时间占比计算**
图39 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var box = utils.getElementsByClass('box')[0];
var duration = 1000; //从起点运动到终点需要1000ms
var target = utils.getsetAttr('clientWidth') - box.offsetWidth; //要运动的距离
var time = 0; //花费的时间
var begin = utils.getCss(box,"left");
var total = target - begin;
var timer = window.setInterval(function (){
time += 5; //每次执行一次定时时间就多花费5ms
if(time >= duration){
window.clearInterval(timer);
utils.setCss(box,"left",target);
return;
}
var curLeft = begin + (time/duration)*total;
utils.setCss(box,"left",curLeft);
},10);
  • [x] 不定时间
    • 步长固定时,总距离/步长=多少步
1
2
3
4
5
6
var speed = 5;
if (curLeft >= maxLeft) {
window.clearInterval(box.timer);
utils.setCss(box, "left", maxLeft);
return;
}
  • 优化一:边界问题:不一定是整数,少走一步多走一步都不能正好到达目标位置,多走了半步,发现超了,又退回去的闪动。
  • 【解决】加步长判断边界:先判断加一步超了吗,超出则直接赋最大值,没超出则再赋加这一步的值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    if (curLeft < target) {
    curLeft += speed;
    if (curLeft >= maxLeft) {
    window.clearInterval(box.timer);
    utils.setCss(box, "left", maxLeft);
    return;
    }
    utils.setCss(box, "left", curLeft);
    }
    //或者
    if (curLeft+step >= maxLeft) {
    window.clearInterval(box.timer);
    utils.setCss(box, "left", maxLeft);
    return;
    }
    curLeft += speed;
    utils.setCss(box, "left", curLeft);

轮循动画(左右反弹动画)

  • [x] 递归思想(setTimeout)

    • [ ] 递归函数不传参(setTimeout递归实现左右反弹动画)
      • 优化二:每次执行前首先把清除上次的定时器
      • timer必须全局,因为每次执行move形成的私有作用域都不同,window.clearTimeout(timer);timer是本次私有作用域下的timer,而不是要清除的上次的timer
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        var step=5;
        var timer = null;//全局
        function move() {
        window.clearTimeout(timer);//清除上次定时器
        var curLeft = utils.getCss(box, "left");
        if (curLeft +step >= maxLeft) {
        utils.setCss(box, "left", maxLeft);
        return;
        }
        curLeft += step;
        utils.setCss(box, "left", curLeft);
        timer = window.setTimeout(move, 10); // move不需传参时,不需要写匿名函数
        }
        move();
  • [ ] 递归函数传参(setTimeout递归实现左右反弹动画)

  • 【问题】存在作用域累积问题导致很多不销毁的私有作用域(每次执行到达时间都需要先执行一次匿名函数(形成私有作用域),在匿名函数中再执行move,move中需要用到的数据值target在第一次执行move方法中,需要把匿名函数形成的私有作用域作为跳板,导致匿名函数形成的私有作用不能销毁)
    图40
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    var box = utils.getElementsByClass('box')[0];
    var maxLeft = utils.getsetAttr('clientWidth') - box.offsetWidth;
    var minLeft = 0;
    var step = 5;
    var timer = null;
    function move(target) {
    window.clearTimeout(timer); //清除上一次点击按钮启动的定时器 ???并没有清除定时器
    var curLeft = utils.getCss(box, "left");
    if (curLeft < target) { //向右
    if (curLeft + step >= maxLeft) {
    utils.setCss(box, "left", maxLeft);
    return;
    }
    curLeft += step;
    utils.setCss(box, "left", curLeft);
    } else if (curLeft > target) { //向左
    if (curLeft - step <= minLeft) { //定时器停止的拦截条件
    utils.setCss(box, "left", minLeft);
    return;
    }
    curLeft -= step;
    utils.setCss(box, "left", curLeft);
    } else { //相等的时候就是原地不动
    return;
    }
    timer = window.setTimeout(function(){
    move(target);
    },10);
    }
    utils.setCss(box, "left", 500);
    // 两次点击move执行形成的私有作用域不同
    document.getElementById('left').onclick = function() {
    move(minLeft);//私有作用域A
    }
    document.getElementById('right').onclick = function() {
    move(maxLeft); //私有作用域A

优化三:作用域累积问题优化
【解决】:需要传参的时,里面创建一个函数_move()不传参需要参数的参数在外层的函数作用域中找,在里面让setTimeout调用小函数_move(),这样就不需要匿名函数来调用传参的move(target),也就不会再形成作用域累积问题
优化四同一元素在同一时间只运行一个动画(下一个动画开始时,首先把上一个动画的定时器清除掉):保证当前元素所拥有动画接收定时器返回值的那个变量需要共享,把这个值放到当前元素的自定义属性上
【解决】共享的定时器放到当前元素的自定义属性上,例如:box.timer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function move(target) {//不销毁
var timer = null;
_move();//作用域执行一次销毁一次
function _move() { //不传参,需要参数target到大函数中找
window.clearTimeout(box.timer); //清除上一次点击按钮启动的定时器
var curLeft = utils.getCss(box, "left");
if (curLeft < target) { //向右
if (curLeft + step >= maxLeft) {
utils.setCss(box, "left", maxLeft);
return;
}
curLeft += step;
utils.setCss(box, "left", curLeft);
} else if (curLeft > target) { //向左
if (curLeft - step <= minLeft) { //定时器停止的拦截条件
utils.setCss(box, "left", minLeft);
return;
}
curLeft -= step;
utils.setCss(box, "left", curLeft);
} else { //相等的时候就是原地不动
return;
}
box.timer = window.setTimeout(_move,10);//
}
}