Draggable object animation in VueJS allows for smooth movement of

I'm currently working on developing draggable objects in Vue.js from scratch, but I've encountered two issues.

  1. After dragging, the object quickly snaps to the target coordinates without any transition effect.
  2. I attempted to eliminate the 'Ghosting image' by changing the opacity to 0% while dragging, but it doesn't seem to be effective.

Below is the code snippet I am currently implementing: https://jsfiddle.net/wmsk1npb/

<div id="app">
  {{x}}/{{y}} ....... {{coordinates}}
        <div class="bubbleMenuContainer" :style="coordinates" draggable="true" @drag="move" @dragend="set">
            Test
        </div>
</div>
new Vue({
  el: '#app',
  data: {
    x:0,
    y:0,
    coordinates:{
         top: "100px",
         left: "100px",
         opacity: "100%",
     }
    },
   methods:{
        move(event){
            this.x =  event.clientX;
            this.y =  event.clientY;
            this.coordinates.left = event.clientX + "px";
            this.coordinates.top = event.clientY + "px";
            this.coordinates.opacity = "0%;"
        },
        set(event){
            this.coordinates.left = event.clientX + "px";
            this.coordinates.top = event.clientY + "px";
            this.coordinates.opacity = "100%;"
        }
    }
})
.bubbleMenuContainer{
    position: absolute;
    border-radius: 100px;
    background-color: lightcoral;
    width: 30px;
    height: 30px;
    padding: 10px;
}

Answer №1

My approach to achieving draggable functionality differs from using draggable="true". Instead, I prefer utilizing mouse events such as up, down, and move (along with touch equivalents for touch-enabled devices).

This method provides greater control and eliminates the issue of overlapping items being displayed.

The concept behind this technique involves:

  • Tracking data like position, start drag position (x,y), and whether the item is currently being dragged
  • Setting isDragging to true and storing the initial drag position coordinates on mouse down event
  • Updating the position based on the difference between start drag and current clientXY if isDragging === true on mouse move
  • Resetting isDragging to false on mouse up event

For enhanced functionality, you can dynamically add and remove mousemove and mouseup listeners. Placing the mouse move listener on the document rather than the element itself is a more effective approach.

I recently wrote about implementing this process using the new Vue3 Composition API at https://dev.to/dasdaniel/vue3-compisition-api-craeting-a-draggable-element-fo6

Although it's specifically for Vue3 and not directly copy-paste friendly, the crucial logic is outlined below:

  const onMouseDown = e => {
    let { clientX, clientY } = e;
    position.dragStartX = clientX - position.x;
    position.dragStartY = clientY - position.y;

    position.isDragging = true;

    document.addEventListener("mouseup", onMouseUp);
    document.addEventListener("mousemove", onMouseMove);
  };

  const onMouseMove = e => {
    let { clientX, clientY } = e;
    position.x = clientX - position.dragStartX;
    position.y = clientY - position.dragStartY;
  };

  const onMouseUp = e => {
    let { clientX, clientY } = e;
    position.isDragging = false;
    position.dragStartX = null;
    position.dragStartY = null;
    document.removeEventListener("mouseup", onMouseUp);
    document.removeEventListener("mousemove", onMouseMove);
  };

Answer №2

The attribute "draggable" in HTML is used to determine if an element can be dragged. This behavior is native to browsers, and you can find more information about it here. The reason you see a "ghost image" when trying to drag the bubble is due to this browser behavior.

A simpler method for making an element draggable can be seen in this sample on JSFiddle here.

<div id="app" @mouseup="up" @mousemove="move">
  {{x}}/{{y}} ....... {{coordinates}}
  <div class="bubbleMenuContainer" :style="coordinates" @mousedown="down">
     Test
  </div>
</div>

down(e) {
  this.bubbleMenuClickState = true;
  this.offset = [
    e.target.offsetLeft - e.clientX,
    e.target.offsetTop - e.clientY
  ];
},
up(e) {
  this.bubbleMenuClickState = false;
},
move(e) {
  if (this.bubbleMenuClickState) {
     this.coordinates.left = (e.clientX + this.offset[0]) + "px";
     this.coordinates.top = (e.clientY + this.offset[1]) + "px";
  }
}

Here are the steps:

  1. Add a "mousedown" listener to calculate the offset of the bubble relative to its parent. Without this step, the initial click on the bubble may set its position unexpectedly since no mouse move event has occurred yet.
  2. Include a "mouseup" listener on the bubble's parent (such as #app) to detect when the user releases the click, preventing further movement caused by mouse events.
  3. Implement a "mousemove" listener to update the bubble's position while the user holds and drags it.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Clearing the Redux state in my app upon exiting the page

I've come across similar inquiries, but none of them quite match my situation. When a user clicks on one of the buttons in my app, it triggers a get request to fetch data and then displays that data on the screen. However, the issue arises when I nav ...

When reference variables are utilized before being parsed, what happens?

I'm a beginner learning Angular and have a question regarding the use of reference variables. Here is an example code snippet: <div class="bg-info text-white p-2"> Selected Product: {{product.value || '(None)'}} </div> <di ...

Creating a series of promises in a structured chain

How can the code structure be improved, especially regarding exception handling within a "promise chain"? $("#save").click(function(e) { e.preventDefault(); let $self = $(this); let profile = {} $self.prop("disabled" ...

Showing text instantly upon clicking a radio button, in real-time

I'm working with a set of radio buttons that are linked to numbers ranging from 1 to 24. I need to show the selected number in another part of the page when a radio button is clicked. What would be the best way to achieve this? ...

Modifying the State array is beyond my control

I'm facing an issue passing the codes correctly here, so I'll explain the problem using images. (REMEMBER: All these are within the same component) Issue: I have a single state value: state = { base: [ {tomato: false}, {egg: true} ], ...

Dealing with a frustrating roadblock in Three.js where you encounter an "Unknown format" error while trying to work with

Greetings, I am relatively new to THREE.js and currently experimenting with loading a .FBX Object using the FBXLoader found in three/examples/jsm/loaders/FBXLoader while integrating this into React.js. Upon launching the page, I encountered an issue where ...

How to set up 'ng serve' command in Angular to automatically open a private browsing window?

I am looking for a way to open my project in an Incognito Mode browser without storing any cache. Is there a specific Angular CLI flag that can be included in the ng serve -o command or in the Angular CLI configuration file to enable opening a browser in ...

Is there a way for me to create a route similar to Instagram's setup, such as localhost:3000

I am looking to structure my routes similar to Instagram (instagram.com/username). Currently, I have implemented the following route: router.get('/:name', loggedin, function (req, res, next) { res.render('profile', {"Request name" ...

The Ajax request fails to send the form files

I'm encountering an issue where a form isn't passing one variable. Here is my table structure: Schema::create('projects', function(Blueprint $table) { $table->increments('id'); $table->string(&apos ...

Transforming JSON keys in Angular

As a newcomer to angular and API integration, I am facing an issue with ngCharts in my project. The chart specifically requires the keys names in JSON to be "value" and "name", but the API I am using provides keys named "count" and "label". Is there a way ...

Issue with Jquery Slider Showing Empty Slide

Currently addressing issues with the slider on my site, which utilizes SlidesJS at . There seems to be a problem where it shows a blank slide inexplicably. After investigating, I noticed that a list element is being added to the pagination class, but I can ...

The design problem arises from using jQuery to dynamically prepare the table body at runtime

The table row and data are not appearing in the correct format. Here is a link to the problem on Fiddle: http://jsfiddle.net/otc056L9/ Below is the HTML code: <table border="1" style="width: 100%" class="eventtable"> <thead style="color: b ...

Ways to display a sorting icon depending on a calculated attribute

My goal is to display the appropriate sorting icon based on a computed value. For instance, if the column1 is clicked, one of two classes, classRefcodeDown or classRefcodeUp, should be true. Both computed properties invoke a method called sortClassRefcode, ...

Generate 2 configurations for webpack

Currently, I am facing a challenge while building a webpack file. The issue arose when I needed to incorporate the 'node' target due to conflicts with an 'fs' function that reads certain files. Subsequently, I decided to split my config ...

Achieving efficient filtering with multiple search parameters using a for loop in Vue Bootstrap

Something strange is happening with my table. I have set up filters in each column, but as soon as I enter any value, all the items disappear without any error message showing up in the console. Let me share the code snippet: <template> <div> ...

Tips for transforming a JSON object (originated from jquery-css-parser) into properly formatted CSS code

Currently, we are utilizing a jQuery plugin in our project to convert CSS to a JSON object. The plugin can be found at the following link: Now, we have a requirement to convert the JSON object back to CSS in string format. Unfortunately, the plugin we ar ...

Error: Disappearing textarea textContent in HTML/TS occurs when creating a new textarea or clicking a button

I've encountered an issue with my HTML page that consists of several textareas. I have a function in place to dynamically add additional textareas using document.getElementById("textAreas").innerHTML += '<textarea class="textArea"></text ...

Implementing model synchronization on server initialization with Next.js and sequelize

When it comes to using Express with React on the backend, I'm accustomed to working in a server.js file to synchronize the database. However, I've recently started working with Next.js and noticed that there's no server.js file to sync the m ...

Create an Interactive Data Visualization with JSON using CanvasJS Charts

Having some trouble creating a chart using CanvasJS. After fetching data from the API and checking the JSON array, I encounter two errors when attempting to generate dataPoints for the graph: "data invalid" on the data field and "NaN" on the value field. ...

I am dynamically generating table rows and populating them with data. However, I encountered an issue with retrieving the data by their respective IDs

Creating a table row dynamically by populating it with data. However, encountering an issue with retrieving the data by their respective id. if(xmlhttp.status == 200) { var adminList = xmlhttp.responseJ ...