simulate a 3 pixel drag on draggable elem

我的未来我决定 提交于 2021-02-18 06:46:05

问题


I am simply trying to simulate a small click and drag on a draggable div elem — I've found several similar questions here on SO but all involving the use of additional plugins...

Is there a plain JavaScript or jQuery ability to handle specifically the drag? as I know .click(); or mouse down can be called to initiate.

I am not trying to create the ability of a drag and drop, I already have that. I am trying to create a small function that simulates this event automatically. click > hold > drag up 3 pixels


Update: Cannot find anything about this on SO or elsewhere without using a third party library, so creating a 500 bounty on it. Surely it's possible.


回答1:


You can simulate mouse events using MouseEvent interface and drag events using the DragEvent interface. You have to trigger the right sequence using EventTarget.dispatchEvent().

I guess that you combine "HTML Drag and Drop API" with "Mouse events" to create a drag and drop behaviour, so I'll provide one to simulate Drag & Drop and one to simulate Mouse capture and Mouse move.

Simulating Mouse capture and Mouse move

Please read inline comments

// We create 3 mouse events using MouseEvent interface,
// one for mousedown to initiate the process,
// one for mousemove to handle to movement
// and one for mouseup to terminate the process

const mouseDownEvent = new MouseEvent('mousedown', {
  clientX: element.getBoundingClientRect().left,
  clientY: element.getBoundingClientRect().top,
  bubbles: true,
  cancelable: true
});

const mouseMoveEvent = new MouseEvent('mousemove', {
  clientX: element.getBoundingClientRect().left + 3,
  clientY: element.getBoundingClientRect().top,
  bubbles: true,
  cancelable: true
});

const mouseUpEvent = new MouseEvent('mouseup', {
  bubbles: true,
  cancelable: true
});

// Dispatch the mousedown event to the element that has the listener
element.dispatchEvent(mouseDownEvent);

// For mousemove, the listener may be the parent or even the document
<element|document>.dispatchEvent(mouseMoveEvent);

// Dispatch mouseup to terminate the process
element.dispatchEvent(mouseUpEvent);

Run the code snippet. The square div element is draggable and you can click and drag it when the simulation timer is not running. Click the simulate button to see the simulation in action:

// Timer ID holder
let linearSimulationTimer;

// Simulation X/Y calculation
let calcX = 0, calcY = 0;

// Simulation X/Y axis orientation to handle parent collisions
let xAxisOrientation = 1, yAxisOrientation = 1;

// How many pixels to move the element for X/Y axis
const pixelsShift = 3;

// Elements
const simulateButton = document.getElementById('simulate-dnm');
const movable = document.getElementById('movable');
const movableContainer = movable.parentNode;

simulateButton.addEventListener('click', function() {  
  // If there is a timer running
  if (linearSimulationTimer) {
    // Stop and clear the timer
    clearInterval(linearSimulationTimer);
    linearSimulationTimer = null;
    
    // Create a simple mouseup event with no custom properties
    const mouseUpEvent = new MouseEvent('mouseup', {
      bubbles: true,
      cancelable: true,
    });
    
    // Dispatch it to the movable element
    movable.dispatchEvent(mouseUpEvent);

    // Handle button label text (start/stop)
    simulateButton.classList.remove('running');
  // Else if there is no timer running
  } else {
    // Create the mousedown event using movable element client left/top for event clientX.clientY
    const mouseDownEvent = new MouseEvent('mousedown', {
      clientX: movable.getBoundingClientRect().left,
      clientY: movable.getBoundingClientRect().top,
      pageX: 0,
      pageY: 0,
      bubbles: true,
      cancelable: true,
      view: window
    });

    // Dispatch the mousedown event to the movable element
    movable.dispatchEvent(mouseDownEvent);

    // Get movable parent client rect
    const parentRect = movable.parentNode.getBoundingClientRect();

    // Start the simulation timer
    linearSimulationTimer = setInterval(() => {
      // Get movable element size
      const { width, height } = movable.getBoundingClientRect();

      // Calculate new X/Y position and orientation
      calcX += pixelsShift * xAxisOrientation;
      calcY += pixelsShift * yAxisOrientation;

      // If we hit movable parent X axis bounds, reverse X axis
      if (calcX + width > parentRect.width) {
        calcX  = parentRect.width - width;
        xAxisOrientation = -xAxisOrientation;
      } else if (calcX < 0) {
        calcX = 0;
        xAxisOrientation = -xAxisOrientation;
      }

      // If we hit movable parent Y axis bounds, reverse Y axis
      if (calcY + height > parentRect.height) {
        calcY  = parentRect.height - height;
        yAxisOrientation = -yAxisOrientation;
      } else if (calcY < 0) {
        calcY = 0;
        yAxisOrientation = -yAxisOrientation;
      }

      // Create mousemove event using calcX/calcY and the parent client position
      const mouseMoveEvent = new MouseEvent('mousemove', {
        clientX: parentRect.left + calcX,
        clientY: parentRect.top + calcY,
        pageX: 0,
        pageY: 0,
        bubbles: true,
        cancelable: true,
        view: window
      });

      // Dispatch the mousemove event to the parent which it has the listener
      movableContainer.dispatchEvent(mouseMoveEvent);
    }, 50);
    
    // Handle button label text (start/stop)
    simulateButton.classList.add('running');
  }
});

// Mouse capture and drag handler (https://javascript.info/mouse-drag-and-drop)
movable.onmousedown = function(event) {
  let shiftX = event.clientX - movable.getBoundingClientRect().left;
  let shiftY = event.clientY - movable.getBoundingClientRect().top;

  moveAt(event.pageX, event.pageY);

  function moveAt(pageX, pageY) {
    movable.style.left = pageX - shiftX - movableContainer.offsetLeft + 'px';
    movable.style.top = pageY - shiftY - movableContainer.offsetTop + 'px';
  }

  function onMouseMove(event) {
    moveAt(event.pageX, event.pageY);
  }

  movableContainer.addEventListener('mousemove', onMouseMove);

  movable.onmouseup = function() {
    movableContainer.removeEventListener('mousemove', onMouseMove);
    movable.onmouseup = null;
  }
}

movable.ondragstart = function() {
  return false;
}
#movable-container {
  position: relative;
  height: 80px;
  width: 200px;
  margin: auto;
  margin-bottom: 20px;
  border: 1px dotted silver;
}

#movable {
  position: relative;
  left: 0;
  width: 30px;
  height: 30px;
  background-color: cornflowerblue;
  border-radius: 5px;
  border: 1px solid grey;
}

#simulate-dnm > span:before {
  content: 'Start ';
}

#simulate-dnm.running > span:before {
  content: 'Stop ';
}
<div id="movable-container">
  <div id="movable"></div>
</div>
<div>
  <button id="simulate-dnm"><span>Mouse capture & move simulation</span></button>
</div>

Simulating Drag n Drop

Please read inline comments

// We create 3 drag events using DragEvent interface,
// one for dragstart to initiate the process,
// one for drop to handle the drag element drop inside the drop container
// and one for dragend to terminate the process

const dragStartEvent = new DragEvent('dragstart', {
  bubbles: true,
  cancelable: true
});
const dragEndEvent = new DragEvent('dragend', {
  bubbles: true,
  cancelable: true
});
const dropEvent = new DragEvent('drop', {
  bubbles: true,
  cancelable: true
});

// Dispatch the dragstart event to the source element to initiate
sourceNode.dispatchEvent(dragStartEvent);

// Dispatch the drop event to the destination element to get the drag
destinationNode.dispatchEvent(dropEvent);

// Dispatch the dragend event to the source element to finish
sourceNode.dispatchEvent(dragEndEvent);

Run the code snippet and hit the simulate button to trigger the drag n drop events sequence:

// The current parent index that the drag element is inside
let currentParentIndex = 0;

// Elements
const simulateButton = document.getElementById('simulate-dnd');
const sourceNode = document.getElementById('drag');

function simulateDragDrop(sourceNode, destinationNode) {
  // Create dragstart event
  const dragStartEvent = new DragEvent('dragstart', {
    bubbles: true,
    cancelable: true
  });
  
  // Create dragend event
  const dragEndEvent = new DragEvent('dragend', {
    bubbles: true,
    cancelable: true
  });
  
  // Create drop event
  const dropEvent = new DragEvent('drop', {
    bubbles: true,
    cancelable: true
  });

  // Dispatch dragstart event to the draggable element
  sourceNode.dispatchEvent(dragStartEvent);
  
  // Dispatch drop event to container element we want to drop the draggable
  destinationNode.dispatchEvent(dropEvent);
  
  // Dispatch dragend  event to the draggable element
  sourceNode.dispatchEvent(dragEndEvent);
}

simulateButton.addEventListener('click', function() {
  // Change drop container index to other container than the current
  const newParentIndex = currentParentIndex === 0 ? 1 : 0;
  
  // Get the drop container element
  const destinationNode = document.getElementsByClassName('dropzone')[newParentIndex];

  // Initiate simulation sequence
  simulateDragDrop(sourceNode, destinationNode);

  // Save the new container index
  currentParentIndex = newParentIndex;
});


// Drag n Drop handling

let dragged;

document.addEventListener("dragstart", function(event) {
  // store a ref. on the dragged elem
  dragged = event.target;
  // make it half transparent
  event.target.style.opacity = .5;
}, false);

document.addEventListener("dragend", function(event) {
  // reset the transparency
  event.target.style.opacity = "";
}, false);

/* events fired on the drop targets */
document.addEventListener("dragover", function(event) {
  // prevent default to allow drop
  event.preventDefault();
}, false);

document.addEventListener("dragenter", function(event) {
  // highlight potential drop target when the draggable element enters it
  if (event.target.className == "dropzone") {
    event.target.style.background = "yellow";
  }
}, false);

document.addEventListener("dragleave", function(event) {
  // reset background of potential drop target when the draggable element leaves it
  if (event.target.className == "dropzone") {
    event.target.style.background = "";
  }
}, false);

document.addEventListener("drop", function(event) {
  // prevent default action (open as link for some elements)
  event.preventDefault();
  // move dragged elem to the selected drop target
  if (event.target.className == "dropzone") {
    event.target.style.background = "";
    dragged.parentNode.removeChild(dragged);
    event.target.appendChild(dragged);
    currentParentIndex = Array.prototype.indexOf.call(event.target.parentNode.children, event.target);
  }  
}, false);
.dropzones {
  display: flex;
  justify-content: space-evenly;
}

.dropzone {
  width: 100px;
  height: 100px;
  background-color: mintcream;
  border-radius: 5px;
  border: 2px dashed darkgrey;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 10px;
}

#drag {
  margin: unset;
  width: 40px;
  height: 40px;
  background-color: coral;
  border-radius: 4px;
  border: 1px solid grey; 
}
<div class="dropzones">
  <div class="dropzone">
    <div id="drag" draggable="true"></div>
  </div>
  <div class="dropzone"></div>
</div>
<div>
  <button id="simulate-dnd">Simulate Drag & Drop</button>
</div>



回答2:


If by 'simulate drag' you mean, trigger a mousedown event and then update the position of the element you should be able to do something like this:

let element = document.getElementById('draggable-div');
element.dispatchEvent(new Event('mousedown'));
element.style.top += 3

That style.top change may or may not move the element depending on how it's positioned (absolute, relative, sticky).

If you have a drag and drop library that you are trying to trigger. Then you might even be able to simulate the drag by simply moving the element into the drop zone (3pixels up) and then firing a mouseup:

let element = document.getElementById('draggable-div');
element.style.top += 3
element.dispatchEvent(new Event('mouseup'));

And the list goes on since there also drag events now (though they are not fully supported yet by all browsers: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API).

But it's not clear from your question what kind of simulation you are going for.



来源:https://stackoverflow.com/questions/61376278/simulate-a-3-pixel-drag-on-draggable-elem

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!