Before l lifted a copy command, this time lifted a drag command.

What kind of snakeskin is it?

Since we are using Element-UI in our project, this directive only applies to the element-UI dialog component. If you are using other UI libraries that require this, it should also work.

In fact, the principle of drag is very simple:

  1. First mouse press (onmousedown)
    • Records the current value of the target elementleftandtop
  2. Mouse movement (onmousemove)
    • Calculate the transverse distance of each movement (disX) and longitudinal distance (disY)
    • And change the elementsleft(left = left + disX) andtop(top = top + disY) value
  3. Release the mouse (onmouseup)
    • Pull off a drag, do some finishing touches

The left and top values are easy to obtain. The key is disX and disY.

Let me generalize a bit:

  • ClientX: indicates the current X coordinate of the mouse

  • ClientY: indicates the current Y coordinate of the mouse

Then the pseudocode is:

  • DisX = clientX when mouse is down – clientX when mouse is released

  • DisY = clientY with mouse down – clientY with mouse off

That’s it. All right, let’s start coding.

// This helper method will be used next to get csS-related property values
const getAttr = (obj, key) = > (
    obj.currentStyle
    ? obj.currentStyle[key]
    : window.getComputedStyle(obj, false)[key]
);

const vDrag = {
    inserted(el) {
      /** * target: the container element of the dialog component ** header: the header of the dialog component */
        const target = el.children[0];
        const header = target.children[0];
        
        // Mouse hand
        header.style.cursor = 'move';
        header.onmousedown = (e) = > {
        
            // Record the coordinates of the mouse and the left and top values of the target element
            const currentX = e.clientX;
            const currentY = e.clientY
            const left = parseInt(getAttr(target, 'left'));
            const top = parseInt(getAttr(target, 'top'));
            
            document.onmousemove = (event) = > {
            
                // Calculate the distance of each mouse movement and change the positioning of the drag element
                const disX = event.clientX - currentX;
                const disY = event.clientY - currentY;
                target.style.left = `${left + disX}px`;
                target.style.top = `${top + disY}px`;
                
                // Prevent the default behavior of the event, can solve the problem of dragging text when selected
                return false; }// The drag ends when the mouse is released
            document.onmouseup = (a)= > {
                document.onmousemove = null;
                document.onmouseup = null; }; }},// Every time the dialog is reopened, restore it
    update(el) {
        const target = el.children[0];
        target.style.left = ' ';
        target.style.top = ' ';
    },
    
    // Clear the event binding at last unmount
    unbind(el) {
        const header = el.children[0].children[0];
        header.onmousedown = null; }};export default vDrap;
Copy the code

That’s the simplest drag and drop. Is that ok? Of course not. What’s the problem with that? If you push too hard and pull the whole thing out of the viewable area, you can’t pull it out.

So I have to perfect it, judge the boundary of the four directions, if you go beyond the boundary, you don’t move. The boundary value is actually the maximum distance that you can drag across the screen which is the maximum of disX and disY

  • On the boundary:target.offsetTop
    • offsetTop: can represent the target element (target) distance the top border from the top of the page
  • Under the border:body.height - target.offsetTop - header.height
    • header.height: Reserved height: indicates that only the dragable area is left outside
  • The left boundary:target.offsetLeft + target.width - 50
    • OffsetLeft: Indicates the distance between the left border of the target element and the left side of the page
    • 50: indicates the reserved width. You can specify the width as long as it is greater than0Can, means to the left no matter how to drag will also stay50pxThe width is on the outside
  • The right boundary:body.width - target.offsetLeft - 50
    • here50Same as above, it means no matter how much you drag it to the left, it will remain. Right50pxThe width is on the outside

There are a number of different ways to compute the boundary, so you can try your own ideas. Then I sketched a diagram to help understand it, although it felt like only I could understand it. Haha…

ok

// ...
// The above code is omitted

header.onmousedown = (e) = > {
    // ...
    // The above code is omitted
    
    // Calculate the boundary values of the four directions
    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) = > {
        // Calculate the distance of each mouse movement and change the positioning of the drag element
        const disX = event.clientX - currentX;
        const disY = event.clientY - currentY;
        
        // Determine the left and right boundaries
        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`;
        }
        
        // Determine the upper and lower boundaries
        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;
    };
}
Copy the code

After registering, it can be used:

<el-dialog v-drag title="Dialog box" :visible.sync="dialogVisible"></el-dialog>
Copy the code

I will not cover the hook function of the directive and how to register it globally. If you are interested, you can read my last article.