最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Why does my JavaScript code for dragging an HTML element distort it at higher dragging speeds? - Stack Overflow

programmeradmin0浏览0评论

I built a small prototype for an UI element with a drag feature and it works, but dragging with medium+ speed results in the dragged element getting quite noticeably distorted along the drag axis. When dragging slowly this does not happen. Does anyone know why this happens here and if it's possible to prevent it? Or does anyone not have this problem maybe? I tried googling but I was completely unable to produce a prompt that would get me an answer.

I'm on Chrome v132.0.6834.160, and here's the JSBin of the mentioned prototype ,css,js,console,output

The red div can be dragged inside the blue div by clicking and then dragging the mouse without letting go, like common drag-and-drop elements.

And since stackoverflow won't let me post it without code, here's only the portion of the code that moves the element.

function slide(e) {
  movy.style.left = Math.max(0,Math.min((e.clientX-offset[0]), (bounds[2]-cSize[0])))+'px';
  movy.style.top = Math.max(0,Math.min((e.clientY-offset[1]), (bounds[3]-cSize[1])))+'px';
}

slide(e) is called from onmousemove. Is setting left / top every tick of the mouse move event known to be a problem for rendering? I also tried wrapping it in a requestAnimationFrame() like follows, but that didn't help.

function slide(e) {
  requestAnimationFrame(function() {                        
  movy.style.left = Math.max(0,Math.min((e.clientX-offset[0]), (bounds[2]-cSize[0])))+'px';
  movy.style.top = Math.max(0,Math.min((e.clientY-offset[1]), (bounds[3]-cSize[1])))+'px';
  });
}

I'm not familiar with the inner workings of requestAnimationFrame at all though, I just know it sometimes helps with similar effects when doing canvas animations.

I built a small prototype for an UI element with a drag feature and it works, but dragging with medium+ speed results in the dragged element getting quite noticeably distorted along the drag axis. When dragging slowly this does not happen. Does anyone know why this happens here and if it's possible to prevent it? Or does anyone not have this problem maybe? I tried googling but I was completely unable to produce a prompt that would get me an answer.

I'm on Chrome v132.0.6834.160, and here's the JSBin of the mentioned prototype https://jsbin.com/lovoqulewa/2/edit?html,css,js,console,output

The red div can be dragged inside the blue div by clicking and then dragging the mouse without letting go, like common drag-and-drop elements.

And since stackoverflow won't let me post it without code, here's only the portion of the code that moves the element.

function slide(e) {
  movy.style.left = Math.max(0,Math.min((e.clientX-offset[0]), (bounds[2]-cSize[0])))+'px';
  movy.style.top = Math.max(0,Math.min((e.clientY-offset[1]), (bounds[3]-cSize[1])))+'px';
}

slide(e) is called from onmousemove. Is setting left / top every tick of the mouse move event known to be a problem for rendering? I also tried wrapping it in a requestAnimationFrame() like follows, but that didn't help.

function slide(e) {
  requestAnimationFrame(function() {                        
  movy.style.left = Math.max(0,Math.min((e.clientX-offset[0]), (bounds[2]-cSize[0])))+'px';
  movy.style.top = Math.max(0,Math.min((e.clientY-offset[1]), (bounds[3]-cSize[1])))+'px';
  });
}

I'm not familiar with the inner workings of requestAnimationFrame at all though, I just know it sometimes helps with similar effects when doing canvas animations.

Share Improve this question asked 18 hours ago johnny2johnny2 234 bronze badges 4
  • 1 Please read more thoroughly: How to create a Minimal, Reproducible Example. “...stackoverflow won't let me post it without code” is not a valid reason for publishing code. The valid reason is purely rational. You have to help us to help you. The code you show alone is not useful enough. For example, you did not show how you handle the event(s)... – Sergey A Kryukov Commented 18 hours ago
  • 1 I dragged it fast, in circles, and switched directions quickly. It works flawlessly, Win 10 Chrome 132.0.6834.160 (Official Build) (64-bit). – zer00ne Commented 17 hours ago
  • 1 I tried the demo, I cannot reproduce. It works perfectly. – code Commented 15 hours ago
  • Alright, has to be something on my end then. Thank you for trying the demo :) – johnny2 Commented 8 hours ago
Add a comment  | 

1 Answer 1

Reset to default 0

In the demo there is an excessive amount of calculations, in the example below is a more simplified version of what I believe you were attempting to do:

  • The user is able to click onto the red square and move it within the blue square while his or her finger is on the button.

  • Get a xy position of the red square relative to the borders of the blue square (I guessed on this since it makes the most sense).

Instead of Mouse events I used Pointer events because it is identical to Mouse events and it can handle other devices such as a pen or user's touch as well. I also used a <form> and some form controls because they are excellent elements for user interaction, but if you prefer the original layout:

const area = document.querySelector(".container");
const rect = document.querySelector(".movy");
// Remove references to ui, io, and xy

Details are commented in the example.

// Reference <form>
const ui = document.forms.ui;
/**
 * Reference all form controls of <form>
 * In this layout it is all:
 *   - <fieldset>
 *   - <output>
 *   - <object>
 */
const io = ui.elements;
// Reference <output>
const xy = io.xy;
// Reference second <fieldset>
const area = io.area;
// Reference <object>
const rect = io.rect;

/**
 * Register fieldset#area to listen for the 
 * "pointerdown" event and call grab().
 * Register fieldset#area to listen for the
 * "dragstart" event and do nothing.
 */
area.addEventListener("pointerdown", grab);
area.addEventListener("dragstart", function(e) {
  return false;
});

/**
 * "pointerdown" event handler registers 
 * fieldset#area to listen for "pointermove" and
 * "pointerup" events and call drag() and drop()
 * respectively.
 * @param {object} e - Event object
 */
function grab(e) {
  this.addEventListener("pointermove", drag);
  this.addEventListener("pointerup", drop);
}

/**
 * "pointermove" event handler moves object#rect
 * to the pointer xy position relative to the
 * borders of fieldset#area.
 * @param {object} e - Event object
 */
function drag(e) {
  /**
   * xy position of pointer relative to the
   * borders of fieldset#area.
   */
  let x = e.offsetX;
  let y = e.offsetY;
  /**
   * xy constraints to keep object#rect within
   * the borders of fieldset#area.
   */
  x = x < 0 ? 0 : x > 500 ? 500 : x;
  y = y < 0 ? 0 : y > 300 ? 300 : y;
  // Move object#rect to the xy position.
  rect.style.left = x + "px";
  rect.style.top = y + "px";
  // Display object#rect xy position.
  xy.value = `x: ${Math.round(x)
                       .toString()
                       .padStart(3, "0")}px 
              y: ${Math.round(y)
                       .toString()
                       .padStart(3, "0")}px`;
}

/**
 * "pointerup" event handler removes the event
 * listeners for "pointermove" and "pointerup" 
 * events.
 * @param {object} e - Event object
 */
function drop(e) {
  this.removeEventListener("pointermove", drag);
  this.removeEventListener("pointerup", drop);
}
:root {
  font: 2ch/1.5 "Segoe UI";
}

.box {
  width: 8rem;
  height: 1.5rem;
  padding-top: 0.75rem;
  user-select: none;
}

#area {
  position: relative;
  width: 500px;
  height: 300px;
  background-color: blue;
}

#rect {
  position: absolute;
  z-index: 1;
  width: 50px;
  height: 50px;
  background-color: red;
  /*
  Prevents #rect from interfering with clicks
  to #area.
  */
  pointer-events: none;
}
<form id="ui">
  <fieldset class="box">
    <output id="xy">x: 000px y: 000px</output>
  </fieldset>
  <fieldset id="area">
    <object id="rect"></object>
  </fieldset>
</form>

发布评论

评论列表(0)

  1. 暂无评论