美文网首页
jQuery之模拟实现$().animate()(上)

jQuery之模拟实现$().animate()(上)

作者: 小进进不将就 | 来源:发表于2019-06-29 17:48 被阅读0次

    根据上图实现除doAnimation外的逻辑:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>jQuery之$().animate()的实现</title>
    </head>
    <body>
    <script src="jQuery.js"></script>
    
      <div id="A" style="width:100px;height:50px;background-color: deeppink">这是A</div>
    
    <script>
      //匿名函数自调用,下面好长好长的function就是$
      //也就是说$是一个function(){xxx}
      (function($) {
    
        window.$ = $;
    
      })(
        //这里也是匿名函数自调用
        //本质就是经过一系列操作得到chenQuery并作为参数$,赋值给window.$
        function() {
          //匹配ID
          let rquickExpr = /^(?:#([\w-]*))$/;
          //jQuery初始化
          function chenQuery(selector) {
            return new chenQuery.fn.init(selector);
          }
    
          //假设是在数据缓存中存取队列
          const Queue=[]
    
          //数据缓存
          const dataPriv={
            get:function (type) {
              if(type==='queue') return Queue
            },
    
          }
    
          const dequeue=function() {
            const Queue=dataPriv.get("queue")
            let fn = Queue.shift()
            //当单个动画结束后,执行下个动画
            const next = function() {
              dequeue();
            }
    
            if ( fn === "inprogress" ) {
              fn = Queue.shift();
            }
    
            if (fn) {
              Queue.unshift( "inprogress" );
              /*执行doAnimation方法,doAnimation(element, options,function() {firing = false;_fire();})*/
              /*fn的参数就是形参func*/
              /*func方法是用来通知上个动画结束,下个动画运行的重要function*/
              //func的作用是用来通知动画执行结束,并继续执行下一个动画
              const func=function() {
                next();
              }
              fn(func);
            }
    
    
          }
    
          //省略type
          const queue=function(element, options, callback, ) {
            //模仿从数据缓存中得到的队列,直接写Queue.push也行
            const Queue=dataPriv.get("queue")
            //向动画队列中添加doAnimation触发器
            Queue.push(function(func) {
              //doAnimation
              callback(element, options, func);
            });
            //如果没有动画在运行,运行动画
            //动画锁inprogress
            if(Queue[0]!=='inprogress'){
              dequeue()
            }
    
          }
    
          /*动画*/
          const animation = function(element,options) {
    
            const doAnimation = function(element, options, func) {
              const width = options.width
    
              /*===这里面定义了动画的算法,也就是Animation实现的地方===*/
              // 默认动画时长2s
              element.style.transitionDuration = '400ms';
              element.style.width =  width + 'px';
    
              /*监听单个动画完结*/
              //transitionend 事件在 CSS 完成过渡后触发
              element.addEventListener('transitionend', function() {
                func()
              });
            }
            //每调用一次animation,就入一次队
            return queue(element, options,doAnimation,);
          }
    
    
          //为chenQuery的fn和prototype原型属性 赋 animate属性
          chenQuery.fn = chenQuery.prototype = {
            //也可以直接把animation里的代码放这里来,但这样就太长了,降低了可读性
            animate: function(options) {
              animation(this.element, options);
              //注意返回的是this,也就是$("#A"),这样就能继续调用animate方法
              // 也就是链式调用
              return this;
            }
          }
          //为chenQuery的fn属性添加init方法
          const init = chenQuery.fn.init = function(selector) {
            // ["#A", "A",groups: undefined,index: 0,input: "#A"]
            const match = rquickExpr.exec(selector);
            //这边默认是只找id的元素
            const element = document.getElementById(match[1])
            //this指chenQuery.fn.init方法
            //为该方法添加element属性
            this.element = element;
            //返回chenQuery.fn.init
            return this;
          }
          //挺绕的,再将init的原型等于chenQuery.fn方法
          init.prototype = chenQuery.fn;
    
          //chenQuery本身是一个function(){}
          // chenQuery{
          //init能调用fn,fn能调用init
          //   fn:{
          //     animate:function(){},
          //     init:function(){},
          //     // init.prototype=fn
          //   },
          //   prototype:{
          //     animate:function(){},
          //   }
          // }
    
          return chenQuery;
        }());
    
    
      const A = document.querySelector('#A');
      //在异步调用中,进行同步调用
      //动画是异步的
      A.onclick = function() {
        //就是连续调用animation.add()
        $('#A').animate({
          'width': '500'
        }).animate({
          'width': '300'
        }).animate({
          'width': '1000'
        });
      };
    
    </script>
    </body>
    </html>
    

    解析:
    (1)匿名函数自调用的参数:

       (function(a){
         console.log(a) //name
       })('name')
      
       (function (b) {
         console.log(b) //function(){console.log('name')}
       })(function () {
         console.log('name')
       })
    

    (2)快速匹配id选择器

          //匹配ID
          let rquickExpr = /^(?:#([\w-]*))$/;
    

    (3)inprogress是动画锁
    当第一个动画执行时,向Queue中添加锁inprogress,阻止异步调用动画,也就是要求同步执行动画,当动画结束时,移除锁,调用下一个动画。

    (4)transitionend
    transitionend事件在 CSS 完成过渡后触发,这里当做单个动画完成的信号,触发后,会告知下个动画进行

    下图的实现将在下篇文章贴出:


    (完)

    相关文章

      网友评论

          本文标题:jQuery之模拟实现$().animate()(上)

          本文链接:https://www.haomeiwen.com/subject/xguaqctx.html