实现一个jQuery拖拽插件

208 阅读1分钟
// 用法:
// var hai = $('.hai_ning');
// hai.drag();
// 自定义配置
/* hai.drag({
    xmax: x轴方向最大边缘,
    xmin: 0,
    ymax: '200px',
    ymin: 0
}); */

$.extend({
  /**
   * 获取元素支持的transform
   * @returns {string}
   */
  getTransform: function() {
    var prefix = ["", "-webkit-", "-moz-", "-o-", "-ms-"];
    var style = document.createElement("div").style;
    var transform = "",
      i = 0,
      len = prefix.length;
    for (; i < len; i++) {
      let t = prefix[i] + "transform";
      if (t in style) {
        return (transform = t);
      }
    }
    return transform;
  },
  /**
   * 获取鼠标位置
   * @param { Event } e 事件对象
   * @returns { object } 坐标
   */
  getControlPos: function(e) {
    let pos = null;
    if (/mouse/.test(e.type)) {
      return (pos = {
        x: e.clientX,
        y: e.clientY
      });
    } else if (/touch/.test(e.type)) {
      return (pos = {
        x: e.pageX,
        y: e.pageY
      });
    }
    return pos;
  }
});
$.fn.extend({
  /**
   * 获取目标元素的位置
   * @returns {object} 坐标
   */
  getTargetPos: function() {
    var pos = {
      x: 0,
      y: 0
    };
    var el = this[0];
    var transform = $.getTransform();
    // 找到浏览器所支持的transform
    if (transform !== "") {
      var transformValue = this.css("transform");
      // 如果没有设置transform
      if (transformValue === "none") {
        el.style[transform] = "translate(0, 0)";
        return pos;
      } else {
        var tmp = transformValue.match(/-?\d+/g); // transformValue: matrix(1, 0, 0, 1, 0, 0)
        return (pos = {
          x: parseInt(tmp[4].trim()),
          y: parseInt(tmp[5].trim())
        });
      }
    } else {
      // 浏览器不支持transform, 则用相对定位方式
      var positionValue = this.css("position");
      if (positionValue === "static") {
        el.style["position"] = "relative";
        return pos;
      } else {
        return (pos = {
          x: parseInt(this.css("left")),
          y: parseInt(this.css("top"))
        });
      }
    }
  },
  /**
   * 设置元素位置
   * @param { object } pos 坐标{x, y}
   * @returns { number } 0: 失败, 1成功
   */
  setTargetPos: function(pos) {
    var edge = this.edge;
    var transform = $.getTransform();
    if (pos.x <= edge.xmin) {
      pos.x = edge.xmin;
    }
    if (pos.x >= edge.xmax) {
      pos.x = edge.xmax;
    }
    if (pos.y <= edge.ymin) {
      pos.y = edge.ymin;
    }
    if (pos.y >= edge.ymax) {
      pos.y = edge.ymax;
    }
    if (transform) {
      this.css('transform', `translate(${pos.x}px, ${pos.y}px)`);
      return 1;
    } else {
      this.css({
        'left': pos.x + "px",
        'top': pos.y + "px"
      });
      return 1;
    }
    return 0;
  },
  /**
   * 拖拽接口函数
   * @param {Object} options {xmax: x轴最大边缘,xmin: x轴最小边缘,ymax: ...ymin: ..}
   */
  drag: function(options) {
    var offset = this.offset();
    var defaults = {
      xmax: document.documentElement.clientWidth - offset["width"] - 1,
      ymax: document.documentElement.clientHeight - offset["height"] - 1,
      xmin: 0,
      ymin: 0
    };
    this.edge = options || defaults;
    var MOBILE = "ontouchstart" in window;
    var target = this;
    var el = this[0];
    var state = {
      moving: false, // 是否正在移动
      startX: 0, // 鼠标的初始位置
      startY: 0,
      sourceX: 0, // 目标元素的初始位置
      sourceY: 0
    };

    /**
     * 重新设置目标状态
     */
    var setTargetState = function() {
      var targetPos = target.getTargetPos();
      state.sourceX = targetPos.x;
      state.sourceY = targetPos.y;
    };
    /**
     * 移动目标
     * @param {Event} e mousemove事件
     */
    var move = function(e) {
      if (!state.moving) {
        return;
      }
      var { x, y } = target.getControlPos(e),
        dx = x - state.startX, // 计算鼠标偏移量
        dy = y - state.startY;
      target.setTargetPos({
        x: (state.sourceX + dx).toFixed(),
        y: (state.sourceY + dy).toFixed()
      });
    };

    /**
     * 放下目标
     */
    var end = function() {
      setTargetState(el);
      state.moving = false;
      if (!MOBILE) {
        $(document).unbind("mousemove", move);
        $(document).unbind("mouseup", end);
      } else {
        $(document).unbind("touchmove", move);
        $(document).unbind("touchend", end);
      }
      console.log("move end");
    };

    /**
     * 抱起目标
     * @param {Event} e mousedown事件
     */
    var start = function() {
      var controlPos = target.getControlPos(e);
      setTargetState(el);
      state.moving = true;
      state.startX = controlPos.x;
      state.startY = controlPos.y;
      if (!MOBILE) {
        $(document).bind("mousemove", move);
        $(document).bind("mouseup", end);
      } else {
        $(document).bind("touchmove", move);
        $(document).bind("touchend", end);
      }
    };
    !MOBILE ? target.on("mousedown", start) : target.on("touchstart", start);
  }
});