面向對象思想-封裝拖拽對象

1、我們常常會通過修改元素的top,left,translate來其的位置發生改變。修改元素的left,top值,但會引起頁面重繪,而transform不會,所以要優先使用transform
2、如何獲取當前瀏覽器支持的transform兼容寫法
3、獲取元素的初始位置:
4、需要綁定的事件:mousedown,mousemove,mouseup
而在移動端,分別與之對應的則是touchstart、touchmove、touchend。
5、原理

當事件觸發時,我們可以通過事件對象獲取到鼠標的精切位置。這是實現拖拽的關鍵。當鼠標按下(mousedown觸發)時,我們需要記住鼠標的初始位置與目標元素的初始位置,我們的目標就是實現當鼠標移動時,目標元素也跟着移動,根據常理我們可以得出如下關係:
移動後的鼠標位置 - 鼠標初始位置 = 移動後的目標元素位置 - 目標元素的初始位置
如果鼠標位置的差值我們用dis來表示,那麼目標元素的位置就等於:
移動後目標元素的位置 = dis + 目標元素的初始位置

通過事件對象,我們可以精確的知道鼠標的當前位置,因此當鼠標拖動(mousemove)時,我們可以不停的計算出鼠標移動的差值,以此來求出目標元素的當前位置。這個過程,就實現了拖拽。

而在鼠標鬆開(mouseup)結束拖拽時,我們需要處理一些收尾工作。

6、封裝原則

如何合理的處理屬性與方法的位置。

  • 構造函數中: 屬性與方法爲當前實例單獨擁有,只能被當前實例訪問,並且每聲明一個實例,其中的方法都會被重新創建一次。

  • 原型中: 屬性與方法爲所有實例共同擁有,可以被所有實例訪問,新聲明實例不會重複創建方法。

  • 模塊作用域中:屬性和方法不能被任何實例訪問,但是能被內部方法訪問,新聲明的實例,不會重複創建相同的方法。

對於方法的判斷比較簡單。

  • 因爲在構造函數中的方法總會在聲明一個新的實例時被重複創建,因此我們聲明的方法都儘量避免出現在構造函數中。

  • 而如果你的方法中需要用到構造函數中的變量,或者想要公開,那就需要放在原型中。

  • 如果方法需要私有不被外界訪問,那麼就放置在模塊作用域中。

對於屬性的判斷

對於屬性放置於什麼位置需要在實際開發中不斷的總結經驗。

  • 如果屬性值只能被實例單獨擁有,比如person對象的name,只能屬於某一個person實例,又比如這裏拖拽對象中,某一個元素的初始位置,也僅僅只是這個元素的當前位置,這個屬性,則適合放在構造函數中。

  • 而如果一個屬性僅僅供內部方法訪問,這個屬性就適合放在模塊作用域中。

模塊封裝代碼:

; //文件合併時,防止前一個文件末尾沒加分號
  (function() {
    //私有屬性,內部使用,不需實例訪問
    var transform = getTransform();

    function Drag(selector) {
      this.elem = typeof selector == 'object' ? selector : document.getElementById(selector);
      this.startX = 0;
      this.startY = 0;
      this.sourceX = 0;
      this.sourceY = 0;
      this.init();
    }

    Drag.prototype = {
      constructor: Drag,
      init: function() {
        this.setDrag();
      },

      getStyle: function(property) {
        return window.getComputedStyle ? window.getComputedStyle(this.elem, null).getPropertyValue(property) : this.currentStyle.getAttribute(property);

      },

      //獲取當前元素的位置信息,注意與之前的不同之處
      getPosition: function() {
        var pos = { x: 0, y: 0 };
        if (transform) {
          var transformValue = this.getStyle(transform);
          if (transformValue == 'none') {
            this.elem.style[transform] = 'translate(0,0)';
          } else {
            var temp = transformValue.match(/-?\d+/g);
            pos = {
              x: parseInt(temp[4].trim()),
              y: parseInt(temp[5].trim())
            }
          }
        } else {
          if (this.getStyle('position') == 'static') {
            this.elem.style.position = 'relative';
          } else {
            pos = {
              x: parseInt(this.getStyle('left') ? this.getStyle('left') : 0),
              y: parseInt(this.getStyle('top') ? this.getStyle('top') : 0)
            }
          }
        }
        return pos;
      },
      // 用來設置當前元素的位置
      setPosition: function(pos) {
        if (transform) {
          this.elem.style[transform] = 'translate(' + pos.x + 'px, ' + pos.y + 'px)';
        } else {
          this.elem.style.left = pos.x + 'px';
          this.elem.style.top = pos.y + 'px';
        }
      },
      setDrag: function() {
        var self = this;
        this.elem.addEventListener('mousedown', start, false);

        function start(event) {
          //鼠標初始位置
          self.startX = event.pageX;
          self.startY = event.pageY;


          var pos = self.getPosition();

          self.sourceX = pos.x;
          self.sourceY = pos.y;

          document.addEventListener('mousemove', move, false);
          document.addEventListener('mouseup', end, false);
        }

        function move(event) {
          // 獲取鼠標當前位置
          var curX = event.pageX;
          var curY = event.pageY;

          // 計算差值
          var disX = curX - self.startX;
          var disY = curY - self.startY;

          self.setPosition({
            x: (self.sourceX + disX).toFixed(),
            y: (self.sourceY + disY).toFixed()
          });
        }

        function end(event) {
          document.removeEventListener('mousemove', move);
          document.removeEventListener('mouseup', end);
          //do other things
        }
      }

    }

    //私有方法
    function getTransform() {
      var transform = '',
        divStyle = document.createElement('div').style,
        transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'oTransform'];

      //通過循環找出瀏覽器識別的那個,in操作符用於判斷是否識別
      for (var i = 0, len = transformArr.length; i < len; i++) {
        if (transformArr[i] in divStyle) {
          return transform = transformArr[i];
        }
      }
      return transform;
    }

    window.Drag = Drag;
  })();

  new Drag('target');
  new Drag('target2');

html:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
  * {
    padding: 0;
    margin: 0;
  }

  #target,
  #target2 {
    width: 50px;
    height: 50px;
    background-color: orange;
    cursor: move;
  }

  #target2 {
    background-color: red;
  }

  </style>
</head>

<body>
  <div style="height:1000px">hha</div>
  <div id="target"></div>
  <div id="target2"></div>
 </body>
</html>

jquery實例方法擴展:

// 通過擴展方法將拖拽擴展爲jQuery的一個實例方法
(function ($) {
  $.fn.extend({
    becomeDrag: function () {
      new Drag(this[0]);
      return this;   // 注意:爲了保證jQuery所有的方法都能夠鏈式訪問,每一個方法的最後都需要返回this,即返回jQuery實例
    }
  })
})(jQuery);

參考鏈接:http://www.jianshu.com/p/b3dee0e84454
http://www.jianshu.com/p/3f97570d22b4

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