vue2元素拖拽,适配dialog,支持多修饰符

let seed = 0;
const ctx = '@@draggableContext';
let isDialog = false;
function handleMousedown(event) {
  // event.preventDefault();
  const el = this;
  const rect = el.getBoundingClientRect();

  Object.assign(el[ctx], {
    el,
    type: 'mousedown',
    rect: rect,
    x: rect.x || rect.left,
    y: rect.y || rect.top,
    dragstartX: event.clientX, // 鼠标按下时坐标
    dragstartY: event.clientY,
    dragendX: void 0, // 鼠标松开时坐标
    dragendY: void 0,
    startX: event.clientX, // 起点坐标
    startY: event.clientY,
    dragging: true,
    isMove: false,
  });

  callback(el);

  window.addEventListener('mousemove', el[ctx]._handleMousemove, false);
  window.addEventListener('mouseup', el[ctx]._handleMouseup, false);
}

function handleMousemove(el) {
  return function (event) {
    if (
      el[ctx].binding.modifiers.dialog &&
      event.target.className.indexOf('el-dialog__header') === -1
    ) {
      return;
    } else {
      if (event.target === document.documentElement) return;

      const current = {
        x: event.clientX,
        y: event.clientY,
      };

      const diff = {
        x: current.x - el[ctx].startX,
        y: current.y - el[ctx].startY,
      };
      if (el[ctx].binding.modifiers.sticky) {
        // 不会拖出屏幕边缘
        const clientWidth = document.documentElement.clientWidth;
        const clientHeight = document.documentElement.clientHeight;

        const {
          x,
          y,
          rect: { width, height },
        } = el[ctx];

        if (diff.x < 0 && x + diff.x <= 0) {
          el[ctx].x = 0;
        } else if (diff.x > 0 && x + width - clientWidth >= 0) {
          el[ctx].x = clientWidth - width;
        } else {
          el[ctx].x += diff.x;
        }

        if (diff.y < 0 && y + diff.y <= 0) {
          el[ctx].y = 0;
        } else if (diff.y > 0 && y + height - clientHeight >= 0) {
          el[ctx].y = clientHeight - height;
        } else {
          el[ctx].y += diff.y;
        }
      } else {
        el[ctx].x += diff.x;
        el[ctx].y += diff.y;
      }

      Object.assign(el[ctx], {
        el,
        type: 'mousemove',
        startX: current.x,
        startY: current.y,
        diffX: diff.x,
        diffY: diff.y,
        isMove: true,
      });

      callback(el);
    }
    // event.preventDefault();
  };
}
function handleMouseup(el) {
  return function (event) {
    // event.preventDefault();

    const lastType = el[ctx].type;

    Object.assign(el[ctx], {
      el,
      type: 'mouseup',
      dragendX: event.clientX, // 鼠标按下时坐标
      dragendY: event.clientY,
      dragging: false,
      isMove: lastType === 'mousemove',
    });

    callback(el);

    window.removeEventListener('mousemove', el[ctx]._handleMousemove, false);
    window.removeEventListener('mouseup', el[ctx]._handleMouseup, false);
  };
}

function callback(el) {
  const bindingFn = el[ctx]?.binding?.value;
  if (typeof bindingFn === 'function') {
    bindingFn({ ...el[ctx], target: el });
  } else {
    const { x, y, rect, dragging } = el[ctx];
    if (!dragging) return;
    el.style.cssText += `
      left: ${x}px;
      top: ${y}px;
      width: ${rect.width}px;
      height: ${rect.height}px;
    `;
  }
}
/**
 * v-draggable
 * @desc
 * 1. v-draggable="handleDraggable" 传入回调函数,根据业务逻辑处理
 * 2. v-draggable.sticky 限制拖拽范围在屏幕内
 * 3. v-draggable.dialog 适配element-ui的dialog
 * @example
 * <div v-draggable>
 * <div v-draggable.sticky>
 * <div v-draggable.dialog>
 * <div v-draggable="handleDraggable">
 *   handleDraggable(config) {
 *   const { type, isMove, dragging } = config;
 *   if (dragging && isMove) {
 *         config.el.style.cssText += `
 *       left: ${config.x}px;
 *       top: ${config.y}px;
 *       width: ${config.rect.width}px;
 *       height: ${config.rect.height}px;
 *     `;
 *         return;
 *       }
 *       if (type === 'mouseup' && !isMove) {
 *       //业务逻辑
 *       }
 *   }
 * ```
 */
export default {
  bind(el, binding, vnode) {
    const id = seed++;
    el[ctx] = {
      id,
      binding,
      vnode,
      _handleMousemove: handleMousemove(el, binding, vnode),
      _handleMouseup: handleMouseup(el, binding, vnode),
    };
    //适配dialog修饰符
    if (el[ctx].binding.modifiers.dialog) {
      let handler = el.querySelector('.el-dialog__header');
      handler.style.cursor = 'move';
    }

    el.addEventListener('mousedown', handleMousedown, false);
  },

  unbind(el) {
    window.removeEventListener('mousemove', el[ctx]._handleMousemove, false);
    window.removeEventListener('mouseup', el[ctx]._handleMouseup, false);
    el.removeEventListener('mousedown', handleMousedown, false);
    delete el[ctx];
  },
};

/**
 * link:https://huaweicloud.csdn.net/63a5624eb878a54545945c8f.html?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Eactivity-2-126022408-blog-123231086.235%5Ev38%5Epc_relevant_anti_t3_base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Eactivity-2-126022408-blog-123231086.235%5Ev38%5Epc_relevant_anti_t3_base&utm_relevant_index=3
 * */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值