前端动画库设计及实现

Author Avatar
墨冥 12月 06, 2016
  • 在其它设备中阅读本文章

作者寄语:该文章讲解动画库设计思想和具体实现方式,欢迎大家到源码查看并提出意见。


一、Usage

  • npm install io-animation

二、测试case

三、测试页面

四、设计目的

  • 功能
    • 提高动画性能,css3 transition实现;
    • 支持动画播放/暂停;
  • 兼容
    • 不支持transition,通过setTimeout实现;
  • 写法
    • 支持链式写法;
    • 支持非链式写法;
  • 执行顺序
    • 支持串行动画;
    • 支持并行动画;
    • 支持串行&并行结合动画(JQuery&Zepto不支持);
    • 支持多dom动画(通过类选择器,默认为并行);
  • 类型
    • 支持普通动画;
    • 支持transform动画;
    • 不支持keyframe动画;
  • 回调
    • 支持串行回调;
    • 支持并行回调;
    • 支持并行动画全部执行完成的回调;

五、案例

  • 单对象串行;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    animate(propDomCallbackBtn1, {
    width: "70%"
    }, {
    duration: 1000,
    easing: "ease",
    cb: function () {
    console.log("串行1");
    }
    })
    .animate(propDomCallbackBtn2, {
    height: 200
    }, {
    duration: 3000,
    easing: "ease",
    cb: function () {
    console.log("串行2");
    }
    });
  • 单对象并行;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    animate(propDomCallbackBtn1, {
    width: "70%"
    }, {
    duration: 1000,
    easing: "ease",
    delay: 2000,
    isAsync: 1
    cb: function () {
    console.log("并行1");
    }
    })
    .animate(propDomCallbackBtn2, {
    height: 200
    }, {
    duration: 3000,
    easing: "ease",
    isAsync: 1
    cb: function () {
    console.log("并行2");
    }
    })
    .endAnimaion(function() {
    console.log("并行动画全部执行完成");
    });
  • 单对象串行&并行;

    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
    animate(propDomCallbackBtn1, {
    width: "70%"
    }, {
    duration: 1000,
    easing: "ease",
    isAsync: 1
    cb: function () {
    console.log("并行1");
    }
    })
    .animate(propDomCallbackBtn2, {
    height: 200
    }, {
    duration: 3000,
    easing: "ease",
    isAsync: 1
    cb: function () {
    console.log("并行2");
    }
    })
    .start(function() {
    console.log("并行动画全部执行完成");
    })
    .animate(propDomCallbackBtn2, {
    height: 200
    }, {
    duration: 3000,
    easing: "ease",
    cb: function () {
    console.log("串行1");
    }
    });
  • 多对象并行;

    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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    animate(propDomCallbackBtn1, {
    width: "70%"
    }, {
    duration: 1000,
    easing: "ease",
    delay: 2000,
    isAsync: 1,
    cb: function () {
    console.log("串行1");
    }
    })
    .animate(propDomCallbackBtn1, {
    height: 200
    }, {
    duration: 3000,
    easing: "ease",
    isAsync: 1,
    cb: function () {
    console.log("串行1");
    }
    })
    .start(function() {
    console.log("并行动画全部执行完成1");
    });
    animate(propDomCallbackBtn2, {
    width: "70%"
    }, {
    duration: 1000,
    easing: "ease",
    delay: 2000,
    isAsync: 1,
    cb: function () {
    console.log("并行3");
    }
    })
    .animate(propDomCallbackBtn2, {
    height: 200
    }, {
    duration: 3000,
    easing: "ease",
    isAsync: 1,
    cb: function () {
    console.log("并行4");
    }
    })
    .start(function() {
    console.log("并行动画全部执行完成2");
    });

六、参数

  • dom: dom元素,可以是一个元素,也可以是一个dom数组,不能为空;
  • property: 属性对象,不能为空;
  • opt: 动画相关参数
    • opt.duration: 动画持续时间,如果不设置默认为0.4s;
    • opt.easing: 动画执行的形式, 如”ease, ease-in, ease-out, ease-in-out, linear, cubic-bezier”, 默认为”linear”;
    • opt.callback: 回调函数,在动画执行完成之后执行,分为两种,一种为单个动画执行完成之后的回调,另一种是所有并行动画执行完成的回调;
    • opt.delay: 延迟时间,如果不设置默认为0s;
    • opt.isAsync: 是否动画是并行,0为串行,1为并行;

七、注意点

  • 动画需要调用start才能开始执行;
  • 非链式写法是创建了多个对象,所以表现为并行动画,不能通过isAsync控制为串行;

八、设计方案概述

  • 存储方案:单个对象串并行动画通过一个队列来进行存储;
  • 多对象动画方案:通过生成多个对象来各自管理;
  • 单对象动画方案:每个对象的串并行动画通过队列存储,以”#”分隔,每两个”#”之间代表一组并行动画,如果只有一个,则表现为串行形式;

九、详细设计方案

  • 关键类

    • 入口类:每次单独(串行方式)调用一个animate函数时都会经过该类,他的主要作用是生成一个新的动画对象,并返回动画管理类,从而支持并行工作和链式调用,下面是其实现:

      入口类

      入口类实现

    • 动画管理类:该类是管理每个对象生成的动画,包含三个主要属性和方法;

      • isRuning:当前动画执行状态,执行中/执行完成;
      • asyncQueue:动画队列,存储串并行动画对象;
      • endCallback:并行动画全部执行完成的回调队列;
      • animate(ele, property, opt):动画开始入口;
      • start(fn):结束并行动画,其中可以传入回调;
      • stop():停止当前动画;

        动画管理类

  • 动画方案时序图:粗略的分为这几个块,starUML不太会用,有的生命周期可能画的不太对,先将就着看看
    动画时序图

    • 第一步:首先一个动画对象进入之后会先初始化一个动画对象;
    • 第二步:初始化后进入动画管理中心,进行单个动画管理;
    • 第三步:动画正式开始之前会对动画的参数进行筛选和处理,比如集合属性的单位('px')处理,浏览器vendor处理('webkit', 'moz', 'ms', 'o', 'Webkit', 'Moz', 'O'),是否支持transition处理等;
    • 第四步:创建动画对象,并将其加入动画队列中;
    • 第五步:开始执行动画;
    • 第六步:动画执行完成之后进行该动画回调;
    • 第七步:所有回调都执行完成之后进行并行动画终止回调的执行;
    • 第八步:如果队列中仍然存在动画对象,执行下一个,跳转至步骤一;

十、设计注意点

  • transitionend多次执行:transitionend执行时机是每一个属性执行完成就会执行,所以你的动画有几个属性,他就会执行几次。这个比较好解决,在执行时remove了transitionend事件监听即可;但是如果多个并行动画执行在一个dom上时就蛋疼了,单纯remove监听是做不到的,可以通过判断动画delay+duration来判断是否是当前动画;
  • 不支持transition怎么办:该实现方案采用的方式是,transition和setTimeout会同时执行,在一次回调处理时会clearTimeout和remove监听,从而做到双保险的作用;

十一、License

MIT License

以上是大概的设计思路,具体可以在上面链接中深入到代码中查看!