Еще одна инструкция Vue для реализации функции перетаскивания

Vue.js

я получил один раньшекоманда копирования, на этот раз с другой командой перетаскивания. .

Что это за змеиная кожа? Наверное, как Джи:

эммм. . Да, выглядит так безвкусно, но никак, шефу нравится.

Поскольку в нашем проекте используетсяelement-ui, вся эта директива нацелена только наelement-uiДиалоговый компонент Ha, если вы используете другиеuiБиблиотека также имеет это требование, и ее также можно использовать для редактирования. .

На самом деле принцип перетаскивания очень прост:

  1. Первый щелчок мыши (onmousedown)
    • записать текущийleftиtopценность
  2. движение мыши (onmousemove)
    • Вычислите боковое расстояние для каждого движения (disX) и продольное расстояние (disY)
    • и измените элементleft(left = left + disXtop(top = top + disY)ценность
  3. отпустить мышь (onmouseup)
    • Завершите перетаскивание и сделайте несколько последних штрихов

leftиtopЗначение легко получить, ключdisXиdisYКак его рассчитать?

Сначала популяризирую:

  • clientX: Указывает текущую координату X мыши.

  • clientY: Указывает текущую координату Y мыши.

Тогда псевдокод:

  • disX= когда нажата мышьclientX- когда мышь отпущенаclientX

  • disY= когда нажата мышьclientY- когда мышь отпущенаclientY

Это так просто, хорошо, давайте начнем кодировать.

// 这个助手方法下面会用到,用来获取 css 相关属性值
const getAttr = (obj, key) => (
    obj.currentStyle
    ? obj.currentStyle[key]
    : window.getComputedStyle(obj, false)[key]
);

const vDrag = {
    inserted(el) {
      /**
       * 这里是跟据 dialog 组件的 dom 结构来写的
       * target: dialog 组件的容器元素
       * header:dialog 组件的头部区域,也是就是拖拽的区域
       */
        const target = el.children[0];
        const header = target.children[0];
        
        // 鼠标手型
        header.style.cursor = 'move';
        header.onmousedown = (e) => {
        
            // 记录按下时鼠标的坐标和目标元素的 left、top 值
            const currentX = e.clientX;
            const currentY = e.clientY
            const left = parseInt(getAttr(target, 'left'));
            const top = parseInt(getAttr(target, 'top'));
            
            document.onmousemove = (event) => {
            
                // 鼠标移动时计算每次移动的距离,并改变拖拽元素的定位
                const disX = event.clientX - currentX;
                const disY = event.clientY - currentY;
                target.style.left = `${left + disX}px`;
                target.style.top = `${top + disY}px`;
                
                // 阻止事件的默认行为,可以解决选中文本的时候拖不动
                return false;
            }
            
            // 鼠标松开时,拖拽结束
            document.onmouseup = () => {
                document.onmousemove = null;
                document.onmouseup = null;
            };
        }
    },
    
    // 每次重新打开 dialog 时,要将其还原
    update(el) {
        const target = el.children[0];
        target.style.left = '';
        target.style.top = '';
    },
    
    // 最后卸载时,清除事件绑定
    unbind(el) {
        const header = el.children[0].children[0];
        header.onmousedown = null;
    },
};

export default vDrap;

Вот и всесамый легкийтащил, так чтоokПока что? Конечно нет, так в чем проблема? То есть, если вы со слишком большим усилием вытащите всю рамку пули из видимой области, вы не сможете ее вытащить.

Так что мы должны улучшить его, судить о границах четырех направлений, и если он превышает граничное значение, он не будет двигаться. Граничное значение на самом деле является максимальным расстоянием, которое можно перетащить на экране, т.е.disXиdisYмаксимальное значение

  • Верхняя граница:target.offsetTop
    • offsetTop: Здесь вы можете представить целевой элемент (target) расстояние верхней границы от верха страницы
  • Нижняя граница:body.height - target.offsetTop - header.height
    • header.height: Зарезервируйте высоту, что означает, что ее можно перетащить вниз, чтобы оставить только перетаскиваемую область снаружи.
  • Левая граница:target.offsetLeft + target.width - 50
    • offsetLeft: Здесь вы можете указать расстояние между левой границей целевого элемента и левой стороной страницы.
    • 50: Указывает зарезервированную ширину, вы можете установить ее самостоятельно, если она больше, чем0Вот именно, это значит, что сколько ни тяни влево, оно останется50pxширина снаружи
  • Правая граница:body.width - target.offsetLeft - 50
    • здесь50То же, что и выше, это означает, что независимо от того, сколько вы перетащите влево, он останется50pxширина снаружи

Здесь есть много способов расчета граничного значения, вы можете попробовать свои собственные идеи. Затем я нарисовал грубую картину, чтобы помочь понять это, хотя мне казалось, что только я могу это понять. Ха-ха. . .

Следующий код используется для реализации оценки границ.okохватывать

// ...
// 以上代码省略

header.onmousedown = (e) => {
    // ...
    // 以上代码省略
    
    // 分别计算四个方向的边界值
    const minLeft = target.offsetLeft + parseInt(getAttr(target, 'width')) - 50;
    const maxLeft = parseInt(getAttr(document.body, 'width')) - target.offsetLeft - 50;
    const minTop = target.offsetTop;
    const maxTop = parseInt(getAttr(document.body, 'height'))
      - target.offsetTop - parseInt(getAttr(header, 'height'));
    
    document.onmousemove = (event) => {
        // 鼠标移动时计算每次移动的距离,并改变拖拽元素的定位
        const disX = event.clientX - currentX;
        const disY = event.clientY - currentY;
        
        // 判断左、右边界
        if (disX < 0 && disX <= -minLeft) {
          target.style.left = `${left - minLeft)}px`;
        } else if (disX > 0 && disX >= maxLeft) {
          target.style.left = `${left + maxLeft}px`;
        } else {
          target.style.left = `${left + disX}px`;
        }
        
        // 判断上、下边界
        if (disY < 0 && disY <= -minTop) {
          target.style.top = `${top - minTop)}px`;
        } else if (disY > 0 && disY >= maxTop) {
          target.style.top = `${top + maxTop}px`;
        } else {
          target.style.top = `${top + disY}px`;
        }
        return false;
    };
}

После регистрации вы можете использовать его:

<el-dialog v-drag title="对话框" :visible.sync="dialogVisible"></el-dialog>

Я не буду вводить хук-функцию инструкции и как ее прописать глобально, если вам интересно, вы можете взглянуть на меня.Предыдущийстатья.