The El-Dialog dialog provided by Element-Plus is very powerful, but it can’t be changed by dragging and dropping. We can implement a drag-and-drop dialog via vue’s custom instructions.

Let’s take a look at the drag effect

www.zhihu.com/zvideo/1380…

Vue’s custom directive directive

Why choose the way of custom instruction to implement? One is easy access to the DOM for manipulation, and the other is easy to use and encapsulate.

There are two ways to register a custom instruction, one is global registration, the other is local registration.

  • Globally register custom directives
app.directive('focus', {
  // When the bound element is inserted into the DOM...
  mounted(el) {
    // Focus the element
    el.focus()
  }
})
Copy the code

From the official website. You’ll need it later when you make plug-ins.

  • Local registration of custom directives
directives: {
  focus: {
    // The definition of a directive
    mounted(el) {
      el.focus()
    }
  }
}
Copy the code

This can be used during the development test phase for easy debugging. Plug-ins are reloaded each time they change, whereas locally registered plug-ins require only local updates.

/ / create a dialogdrag and drag function in mounted.

Analyze the Element-Plus Dialog Dialog

To implement drag-and-drop functionality, you first need to understand the structure of the Dialog Dialog rendered before you can tailor it accordingly. The analysis shows the following structure:

Simply put, three divs are placed in one div, and the effect of “center” is realized by margin (top, left). In other words, if we want to implement the drag-and-drop function, we can change the way of margin-left and margin-top.

Three functions of the mouse

The mouse events onMouseDown, onMousemove, and onMouseup are essential when it comes to drag and drop.

  • onmousedown

Record the coordinates of the cursor when the mouse is pressed down and enter the drag state.

  • onmouseup

Record the coordinates of the cursor when the mouse is raised to end the dragging state.

  • onmousemove

Press and drag the mouse trigger, calculate the offset of the cursor, modify the dialog box margin to achieve the effect of drag.

The implementation code

I wanted to write a general purpose dialog, but the structure rendered by the dialog box is complex and not general enough, so I implemented the drag function for el-Dialog first.

  app.directive('dialogdrag', {
    // Render complete
    mounted(el, binding) {
      // binding.arg
      // binding.value
      // The width of the visible window
      const clientWidth = document.documentElement.clientWidth
      // The height of the viewwindow
      const clientHeight = document.documentElement.clientHeight
      // Record the coordinates
      let domset = {
        x: clientWidth / 4.// Default width is 50%
        y: clientHeight * 15 / 100  // Calculate according to 15vh
      }

      // Popover container
      const domDrag = el.firstElementChild.firstElementChild
      // Reset the top and left distances
      domDrag.style.marginTop = domset.y + 'px'
      domDrag.style.marginLeft = domset.x + 'px'

      // Record the cursor coordinates where the drag starts, with 0 indicating no drag
      let start = { x: 0.y: 0 }
      // Record offset while moving
      let move = { x: 0.y: 0 }

      // Mouse down to start dragging
      domDrag.onmousedown = (e) = > {
        // Check whether the dialog box is reopened
        if (domDrag.style.marginTop === '15vh') {
          // Re-open and set domset.y top
          domset.y = clientHeight * 15 / 100
        }
        start.x = e.clientX
        start.y = e.clientY
        domDrag.style.cursor = 'move' // Change the cursor shape
      }

      // Mouse movement, real-time tracking
      domDrag.onmousemove = (e) = > {
        if (start.x === 0) { // Not a drag state
          return
        }
        move.x = e.clientX - start.x
        move.y = e.clientY - start.y

        // Initial position + drag distance
        domDrag.style.marginLeft = ( domset.x + move.x )  + 'px'
        domDrag.style.marginTop = ( domset.y + move.y )  + 'px'
      }
      // Mouse up to finish dragging
      domDrag.onmouseup = (e) = > {
        move.x = e.clientX - start.x
        move.y = e.clientY - start.y

        // Record the new coordinates as the initial position for the next drag
        domset.x += move.x
        domset.y += move.y
        domDrag.style.cursor = ' ' // Restore the cursor shape
        domDrag.style.marginLeft = domset.x + 'px'
        domDrag.style.marginTop = domset.y + 'px'
        // End drag
        start.x = 0}}})Copy the code
  • Reposition dialog box

The default top is 15vh, which is 15% from the top, so you need to convert to pixels using clientHeight * 15/100. The default left is 50%, so it needs to be converted to pixels with clientWidth / 4.

One small problem with this is that when the window size changes, the left distance does not change.

  • Record position coordinates and offsets

First record the distance of the dialog box, and then record the offset generated when dragging.

  1. Domset records the initial coordinates of the dialog box.
  2. Start records the cursor position at the start of the drag.
  3. Move Indicates the offset of the cursor during drag.
  • Click onmousedown

Press the mouse, indicating the start of drag, at this time we need to record the cursor position. In addition, a small problem was found in the test. When closing the dialog box, there was a transition animation, and then when opening the dialog box for drag, it flew away.

After looking for the reason, I found that the top would be changed to 15Vh when the transition animation was closed, which was inconsistent with the top after we dragged it.

So you need to make a judgment call when you press the mouse. If the initial state is restored, the y-coordinate of the domset needs to be changed. You don’t need to change the x-coordinate, because the transition animation didn’t change left.

  • Move the mouse onMousemove

As we move the mouse, we can get the position of the cursor, minus the initial cursor position, which is how far the dialog box should move. Then we use the dialog box’s initial coordinates + offset to get the dialog box’s new position coordinates. This implements drag and drop of the dialog box.

  • Lift up the mouse onmouseup

We can’t keep dragging, so we need an end action. When we lift the mouse, we can think of the end of dragging, at this point we record the new position of the dialog box, and then set start.x = 0 to end dragging.

Make it a plug-in for reuse

Finally, we made the drag-and-drop feature into a plugin to facilitate global registration.

Create a JS file

// dialogDrag.js

const dialogDrag = (app, options) = > {
  app.directive('dialogdrag', {
    // The definition of a directive
    mounted(el, binding){same as above, omitted... }export default dialogDrag
Copy the code

Then mount the plugin in main.js.

/ / drag
import dialogDrag from './control-web/js/dialogDrag.js'

createApp(App).use(dialogDrag) // Drag the dialog box

Copy the code

use

I wanted to put it directly inside the el-Dialog, but it didn’t work, so I had to wrap a div around it.

<div v-dialogdrag>
    <el-dialog
      title="Shipping address"
      v-model="dialogFormVisible"
      :modal="false"
    >A little...</el-dialog>
  </div>
Copy the code

Notice that we add v-, v-dialogDrag.

The source code

Gitee.com/naturefw/nf…

/src/control-web/js/dialogDrag.js

Gitee.com/naturefw/nf…