SoFunction
Updated on 2025-04-13

Implement high-performance drag and drop instructions based on Vue3

Preface

In modern front-end development, drag and drop function is one of the important means to enhance user experience. This article will introduce in detail how to encapsulate a drag and drop instruction (v-draggable) in Vue 3 and demonstrate its implementation process through practical examples. With this tutorial, you will not only master the basic drag and drop capabilities, but also learn how to optimize instructions to improve their performance and flexibility, thereby adding color to your project.

Package drag and drop command ideas

We will encapsulate a simple drag and drop instruction called v-draggable, which allows us to add drag and drop functionality on any element.

Instruction logic

1. Listen to mouse events: We need to listen to mousedown, mousemove and mouseup events.

2. Calculate drag position: Update the position of the element according to the distance the mouse moves.

3. Clean up events: Remove the event listener after the drag is finished.

Implementation steps

Step 1: Create a command file

Create a folder named directives in the src directory and create a file in it:

// src/directives/
export default {
  mounted(el) {
     = 'absolute';

    let startX, startY, initialMouseX, initialMouseY;

    const mousemove = (e) => {
      const dx =  - initialMouseX;
      const dy =  - initialMouseY;
       = `${startY + dy}px`;
       = `${startX + dx}px`;
    };

    const mouseup = () => {
      ('mousemove', mousemove);
      ('mouseup', mouseup);
    };

    ('mousedown', (e) => {
      startX = ;
      startY = ;
      initialMouseX = ;
      initialMouseY = ;
      ('mousemove', mousemove);
      ('mouseup', mouseup);
      ();
    });
  }
};

Step 2: Registration Instructions

Register this command in the file in the src directory:

import { createApp } from 'vue';
import App from './';
import draggable from './directives/draggable';

const app = createApp(App);

('draggable', draggable);

('#app');

Step 3: Use the command

Now we can use this drag and drop instruction in any component. Edit the src/ file:

<template>
  <div>
    <h1>Vue 3 Drag and drop command example</h1>
    <div v-draggable class="draggable-box">Drag me!</div>
  </div>
</template>

<script>
export default {
  name: 'App'
};
</script>

<style>
.draggable-box {
  width: 150px;
  height: 150px;
  background-color: lightblue;
  text-align: center;
  line-height: 150px;
  cursor: move;
  user-select: none;
}
</style>

Optimize drag and drop commands

The current drag and drop instructions can basically implement the drag and drop function, but there are still some details that need to be optimized, such as:

1. Limit drag range

2. Support touch devices

3. Add throttling to optimize performance

4. Provide some configuration options

Limit drag range

We can prevent the element from being dragged out of the specified range by limiting its position. Here we assume that dragging and dropping is restricted within the parent element.

// src/directives/

export default {
  mounted(el) {
     = 'absolute';

    let startX, startY, initialMouseX, initialMouseY;

    const mousemove = (e) => {
      const dx =  - initialMouseX;
      const dy =  - initialMouseY;

      let newTop = startY + dy;
      let newLeft = startX + dx;

      // Limit drag range within the parent element      const parentRect = ();
      const elRect = ();

      if (newLeft < 0) {
        newLeft = 0;
      } else if (newLeft +  > ) {
        newLeft =  - ;
      }

      if (newTop < 0) {
        newTop = 0;
      } else if (newTop +  > ) {
        newTop =  - ;
      }

       = `${newTop}px`;
       = `${newLeft}px`;
    };

    const mouseup = () => {
      ('mousemove', mousemove);
      ('mouseup', mouseup);
    };

    ('mousedown', (e) => {
      startX = ;
      startY = ;
      initialMouseX = ;
      initialMouseY = ;
      ('mousemove', mousemove);
      ('mouseup', mouseup);
      ();
    });
  }
};

Support touch devices

To support touch devices, we need to add touchstart, touchmove and touchend event listeners.

// src/directives/

export default {
  mounted(el) {
     = 'absolute';

    let startX, startY, initialMouseX, initialMouseY;

    const move = (e) => {
      let clientX, clientY;
      if () {
        clientX = [0].clientX;
        clientY = [0].clientY;
      } else {
        clientX = ;
        clientY = ;
      }

      const dx = clientX - initialMouseX;
      const dy = clientY - initialMouseY;

      let newTop = startY + dy;
      let newLeft = startX + dx;

      const parentRect = ();
      const elRect = ();

      if (newLeft < 0) {
        newLeft = 0;
      } else if (newLeft +  > ) {
        newLeft =  - ;
      }

      if (newTop < 0) {
        newTop = 0;
      } else if (newTop +  > ) {
        newTop =  - ;
      }

       = `${newTop}px`;
       = `${newLeft}px`;
    };

    const up = () => {
      ('mousemove', move);
      ('mouseup', up);
      ('touchmove', move);
      ('touchend', up);
    };

    const down = (e) => {
      startX = ;
      startY = ;
      if () {
        initialMouseX = [0].clientX;
        initialMouseY = [0].clientY;
      } else {
        initialMouseX = ;
        initialMouseY = ;
      }
      ('mousemove', move);
      ('mouseup', up);
      ('touchmove', move);
      ('touchend', up);
      ();
    };

    ('mousedown', down);
    ('touchstart', down);
  }
};

Add throttling optimization performance

To prevent mousemove and touchmove events from being triggered too frequently, we can use throttle techniques to optimize performance.

// src/directives/

function throttle(func, limit) {
  let lastFunc;
  let lastRan;
  return function (...args) {
    const context = this;
    if (!lastRan) {
      (context, args);
      lastRan = ();
    } else {
      clearTimeout(lastFunc);
      lastFunc = setTimeout(function () {
        if (() - lastRan >= limit) {
          (context, args);
          lastRan = ();
        }
      }, limit - (() - lastRan));
    }
  };
}

export default {
  mounted(el) {
     = 'absolute';

    let startX, startY, initialMouseX, initialMouseY;

    const move = throttle((e) => {
      let clientX, clientY;
      if () {
        clientX = [0].clientX;
        clientY = [0].clientY;
      } else {
        clientX = ;
        clientY = ;
      }

      const dx = clientX - initialMouseX;
      const dy = clientY - initialMouseY;

      let newTop = startY + dy;
      let newLeft = startX + dx;

      const parentRect = ();
      const elRect = ();

      if (newLeft < 0) {
        newLeft = 0;
      } else if (newLeft +  > ) {
        newLeft =  - ;
      }

      if (newTop < 0) {
        newTop = 0;
      } else if (newTop +  > ) {
        newTop =  - ;
      }

       = `${newTop}px`;
       = `${newLeft}px`;
    }, 20);

    const up = () => {
      ('mousemove', move);
      ('mouseup', up);
      ('touchmove', move);
      ('touchend', up);
    };

    const down = (e) => {
      startX = ;
      startY = ;
      if () {
        initialMouseX = [0].clientX;
        initialMouseY = [0].clientY;
      } else {
        initialMouseX = ;
        initialMouseY = ;
      }
      ('mousemove', move);
      ('mouseup', up);
      ('touchmove', move);
      ('touchend', up);
      ();
    };

    ('mousedown', down);
    ('touchstart', down);
  }
};

Provide configuration options

Finally, we can provide some configuration options through the parameters of the directive, such as whether to restrict dragging and dropping within the parent element.

      const dx = clientX - initialMouseX;
      const dy = clientY - initialMouseY;

      let newTop = startY + dy;
      let newLeft = startX + dx;

      if (limitToParent) {
        const parentRect = ();
        const elRect = ();

        if (newLeft < 0) {
          newLeft = 0;
        } else if (newLeft +  > ) {
          newLeft =  - ;
        }

        if (newTop < 0) {
          newTop = 0;
        } else if (newTop +  > ) {
          newTop =  - ;
        }
      }

       = `${newTop}px`;
       = `${newLeft}px`;
    }, 20);

    const up = () => {
      ('mousemove', move);
      ('mouseup', up);
      ('touchmove', move);
      ('touchend', up);
    };

    const down = (e) => {
      startX = ;
      startY = ;
      if () {
        initialMouseX = [0].clientX;
        initialMouseY = [0].clientY;
      } else {
        initialMouseX = ;
        initialMouseY = ;
      }
      ('mousemove', move);
      ('mouseup', up);
      ('touchmove', move);
      ('touchend', up);
      ();
    };

    ('mousedown', down);
    ('touchstart', down);
  }
};

Use configuration options

Now we can control whether to limit the drag range by passing parameters when using instructions. For example, edit src/:

&lt;template&gt;
  &lt;div&gt;
    &lt;h1&gt;Vue 3 Drag and drop command example&lt;/h1&gt;
    &lt;div v-draggable:limit class="draggable-box"&gt;Drag me!&lt;/div&gt;
    &lt;div v-draggable class="draggable-box" style="margin-top: 200px;"&gt;I can drag out the container&lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  name: 'App'
};
&lt;/script&gt;

&lt;style&gt;
.draggable-box {
  width: 150px;
  height: 150px;
  background-color: lightblue;
  text-align: center;
  line-height: 150px;
  cursor: move;
  user-select: none;
  margin-bottom: 20px;
}
&lt;/style&gt;

In the example above, the first div uses the v-draggable:limit directive, which means that its drag range will be limited to the parent element. The second div has no such limit and can be dragged freely.

Summarize

Through the detailed explanation of this article, we have successfully implemented and optimized a powerful drag and drop instruction v-draggable. This instruction not only supports mouse operation, but is also compatible with touch devices, and effectively improves performance through a throttling mechanism. In addition, we have implemented the function of limiting the drag and drop range, so that this instruction can adapt to more complex application scenarios. Hope this article helps you understand and master the encapsulation and optimization techniques of custom instructions in Vue 3.

This is the end of this article about implementing high-performance drag and drop instructions based on Vue3. For more related contents of Vue3 drag and drop instructions, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!