jQuery之模擬實現$().animate()(上)

根據上圖實現除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 完成過渡後觸發,這裏當做單個動畫完成的信號,觸發後,會告知下個動畫進行

下圖的實現將在下篇文章貼出:


(完)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章