Sunday, July 29, 2018

JS HTML5 Drag and Drop: Custom Dock Effect Jumping Around in Chrome

Leave a Comment

Situation: I'm using HTML5 drag-and-drop to place tiles in a game I'm writing. I'd like to add an effect where the two tiles that I'm about to drop a new tile between move slightly apart to indicate that this is where you're dropping (similar to the Mac OS dock).

My Approach: I have a flexbox into which I'm dropping these tiles. I wrote a function that essentially returns one period of a sine wave and I'm using it to update the dropped tiles' right: and top: CSS properties (the tiles are position: relative;) based on their original position relative to the mouse during drag.

  // Update occupant style for desired effect   occupants.forEach(function(occupant, index) {     $(occupant).css({'right' : -10 * nudgeSine(occupantsMouseOffset[index] * 10) + 'px',                      'top' : -10 * Math.abs(nudgeSine(occupantsMouseOffset[index] * 10)) + 'px',                      'opacity' : 1 - Math.abs(nudgeSine(occupantsMouseOffset[index])) });   });    // Function to return 1 period of a sine wave   function nudgeSine(x) {     if (x < -3.14159 || x > 3.14159) {       return 0;     } else {       return Math.sin(x);     }   } 

Problem: In Chrome (but not in Firefox), at some mouse positions, which I can't find a pattern in, the tile is jumping back-and-forth. See the .gif below:

In Chrome (left) and in Firefox (right):

demo in Chrome demo in Firefox

I even console.logged the element's calculated right: property, and while it is shown jumping around on screen, it outputs as a constant value.

What I've Tried/Thought About:

  • Even with the mouse stationary and console.log(event.clientX) outputting a constant value, the tile will jump around.
  • I thought event.clientX might be changing imperceptibly, so I'm basing my calculations on Math.trunc(event.clientX) to no avail.
  • I am using element.getBoundingClientRect() in my calculations, which I'm not very familiar with, and I think it may be the root cause of my problem.

I made this CodePen, but wasn't able to completely replicate the issue. Still, I think someone may be able to spot what's happening.

Edit: I've put this up on a github page to fully replicate. This link may not work for future readers of the question, but I'll keep it up for the foreseeable future. To demonstrate the issue, view in Chrome and Firefox.

Thank you.

1 Answers

Answers 1

Perhaps I can expand my answer later, but for now:

Related questions: How to keep child elements from interfering with HTML5 dragover and drop events? 'dragleave' of parent element fires when dragging over children elements

This is what happens: - you start dragging the operator - operator moves over the box, existing operators move along nicely - you move the operator over one of the existing operators - at this point the browser enters a kind of infinite loop thingy, because each time the elements move the position of the elements have to be updated again (because new events are triggered)

Since you need the click event on the existing operators you can't just set them to pointer-events: none; like in the related question, but you can add a class when you start dragging and apply this style to the operators while you're dragging.

Another solution would be to use a library, in the comments of an answer I found the library https://bensmithett.github.io/dragster/, I use draggable by shopify.

update

I wasn't able to find the exact term of this behavior, perhaps we could go with "cyclic case" or "undefined behaviour". See my examples:

:root {    /*colors by clrs.cc*/    --navy: #001f3f;    --blue: #0074D9;    --red: #FF4136;    font-family: sans-serif;  }    .animated {    transition: all .5s;  }    h2 {    color: var(--red);  }    div {    height: 160px;    width: 160px;    padding: 20px;    background: var(--blue);    margin-bottom: 20px;  }    .box1 {    border-right: 20px solid var(--navy);  }    .box1:hover {    border-right: 0px solid var(--navy);  }    .box2:hover {    border-radius: 100px;  }
<div class="box1 animated">hover your mouse over my border on the right →</div>  <div class="box2 animated">hover your mouse over an edge of this box</div>  <h2>Warning, the following boxes have no animations, flashes are expected:</h2>  <div class="box1">hover your mouse over my border on the right →</div>  <div class="box2">hover your mouse over an edge of this box</div>

When the user moves the mouse onto the border the following happens in a loop:

  1. box1 is being hovered
  2. hover styles apply, the border is removed
  3. box1 isn't being hovered
  4. hover styles stop applying, the border is readded

basically for the moment the CSS doesn't really evaluate, because as soon as it evaluates the evaluation is invalid. This is exactly what happens in your example. I don't know whether the CSS standard has rules that define how browsers should handle this. If the expected behavior is defined, either FF or Chrome is wrong and you can file a bug after you find out which browser's behavior is wrong. If no expected behavior is defined and the implementation is left open to browsers then both browsers are right.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment