javascript - 模拟可拖动元素上的 3 像素拖动

标签 javascript jquery ecmascript-6 kendo-ui

我只是想模拟一个小的点击并拖动可拖动的 div elem - 我在这里发现了几个类似的问题,但都涉及使用额外的插件......

是否有简单的 JavaScript 或 jQuery 能力来专门处理拖动?据我所知 .click(); 或鼠标按下可以被调用来启动。

我并不是想创建拖放功能,我已经拥有了。我正在尝试创建一个自动模拟此事件的小函数。 单击 > 按住 > 向上拖动 3 个像素

<小时/>

更新:如果不使用第三方库,就无法在 SO 或其他地方找到有关此内容的任何内容,因此对其创建 500 赏金。当然有可能。

最佳答案

您可以使用 MouseEvent 模拟鼠标事件使用 DragEvent 的界面和拖动事件界面。您必须使用 EventTarget.dispatchEvent() 触发正确的序列.

我猜你结合了 "HTML Drag and Drop API""Mouse events"创建拖放行为,因此我将提供一个来模拟拖放行为,并提供一个来模拟鼠标捕获和鼠标移动。

模拟鼠标捕获和鼠标移动

请阅读内嵌评论

// 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);

运行代码片段。方形 div 元素是可拖动的,当模拟计时器未运行时,您可以单击并拖动它。单击模拟按钮查看运行中的模拟:

// 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>

模拟拖放

请阅读内嵌评论

// 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);

运行代码片段并点击模拟按钮来触发拖放事件序列:

// 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>

关于javascript - 模拟可拖动元素上的 3 像素拖动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61376278/

相关文章:

javascript - 使用 ES6 模块导出/导入单类方法?

javascript - 在 Chrome 上执行多个选择器时 jQuery 中的 INVALID_NODE_TYPE_ERR

javascript - ES6 类与 ES5 风格有多少不同?

javascript - 检测 Web 应用程序中同一 session 的多个选项卡或窗口

javascript - 在插入的手动 div 上应用样式

javascript - 迭代 react 组件

Javascript/jQuery - 将值添加到整数会更改其他变量

javascript - ExtJS:验证整小时时间

javascript - 关于Jscolor如何申请不同的ID

typescript - "Variable ' X ' can be made constant", 'constant' 是什么意思?