1

I need to apply some sliding animation on the .draggable items below whenever a reordering happens. The changes are seen immediately after the order changes, I want to add a delay.

function listItemDragged(e) {
  e.target.classList.add("dragging");
  let dropTarget =
    document.elementFromPoint(e.clientX, e.clientY) === null
      ? e.target
      : document.elementFromPoint(e.clientX, e.clientY);

  if (e.target.parentNode === dropTarget.parentNode) {
    dropTarget =
      dropTarget !== e.target.nextSibling ? dropTarget : dropTarget.nextSibling;
    e.target.parentNode.insertBefore(e.target, dropTarget);
  }
}

function listItemDropped(e) {
  e.target.classList.remove("dragging");
  e.preventDefault();
}

function onLoad() {
  let listItems = document.querySelectorAll(".draggable");
  Array.prototype.map.call(listItems, (option) => {
    option.ondrag = listItemDragged;
    option.ondragend = listItemDropped;
  });
  document.querySelector('.sortable-list').addEventListener("dragover", (e) => e.preventDefault());
}

onLoad();
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Roboto', sans-serif
}

body {
    background-color: #2b3035;
}

.draggable {
    display: flex;
    margin-top: 10px;
    padding: 10px 12px;
    border-radius: 5px;
    border: 1px solid #5c636a;
    margin-right: 5px;
    background-color: #212529;
    cursor: grab;
    color: #ffffff;
    touch-action: none
}

.dragging {
    cursor: grabbing;
    background: transparent;
    color: transparent;
    border: none;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" />

<ul class='sortable-list list-unstyled'>
  <li class='draggable' draggable='true'>
    Lorem ipsum dolor sit amet 1
  </li>
  <li class='draggable' draggable='true'>
    Lorem ipsum dolor sit amet 2
  </li>
  <li class='draggable' draggable='true'>
    Lorem ipsum dolor sit amet 3
  </li>
  <li class='draggable' draggable='true'>
    Lorem ipsum dolor sit amet 4
  </li>
</ul>

The desired effect resembles the below.

desired result

I tried multiple suggestions from here, the question was asked 10 years ago, so some answers no longer work or do but they produce different effects than the one shown above. I got a suggestion by @dalelandry to use sortable.js, while this may produce more or less the desired effects, the integration with frameworks other than node.js can be a bit cumbersome and may not suit my case.

nlblack323
  • 155
  • 1
  • 10
  • As for your error, remove the script tag in your HTML and just call the onLoad() method at the end of your JS section, this will remove your snippit error. – dale landry Apr 05 '23 at 01:49
  • So the GIF is from Bootstrap 5 Drag and drop plugin which you are not using, at least in your snippit. Have you attempted to follow their documentation and use their cdn? Pretty sure you are using JS draggable API which is consistent with how yours looks. This link to [webdev drag and drop](https://web.dev/drag-and-drop/) may assist you. – dale landry Apr 06 '23 at 00:02
  • @dalelandry Viewing the code requires an active PRO account, and the JS source files are minified so there's not much insights gained by saving them which I tried. I checked the link you suggested and it results in the same stiff effect I'm trying to avoid. – nlblack323 Apr 06 '23 at 00:17
  • Gotcha, have you tried using bootstraps version? – dale landry Apr 06 '23 at 00:31
  • @dalelandry what do you mean by bootstraps version? – nlblack323 Apr 06 '23 at 00:40
  • Try this [Fiddle](https://jsfiddle.net/djibe89/d2fvqpke/) – dale landry Apr 06 '23 at 01:19
  • @dalelandry this is better. How can the transition speed be slowed down to match the gif's? – nlblack323 Apr 06 '23 at 01:25
  • see the `100` on the fiddle, change this to your desired speed, remember 100 = 100ms, where 1000 = 1s, play around with the fiddle, if you break it, refresh the page and try again ;) – dale landry Apr 06 '23 at 01:29
  • @dalelandry the transition works fine. Can the dragged item's shadow/placeholder be hidden and shown like the gif? – nlblack323 Apr 06 '23 at 01:37
  • Check out [Sortables documentation](https://github.com/SortableJS/Sortable) – dale landry Apr 06 '23 at 02:11

1 Answers1

1

You can create a smooth animation effect using CSS transition while reordering elements in a list with drag and drop functionality.

enter image description here

The following is a sample code snippet for the moveWithAnimation function, which handles the animation and repositioning of list items:

function moveWithAnimation(target, dropTarget) {
  // dropTarget is not null or dnd move direction is down  
  const moveDirection = !dropTarget || dropTarget.previousElementSibling === target.nextElementSibling;
  const animationTarget = moveDirection ? 
        (dropTarget ? dropTarget.previousElementSibling : target.nextElementSibling) : target.previousElementSibling;

  // animation
  animationTarget.style.transform = `translateY(${moveDirection ? '-100%' : '100%'})`;
  animationTarget.style.transition = "transform .3s";
  animationTarget.ontransitionend = () => {
    animationTarget.style.transition = "";
    animationTarget.style.transform = "";
    target.parentNode.insertBefore(target, dropTarget);
  };
}

Here is the complete sample code:

function moveWithAnimation(target, dropTarget) {
  // dropTarget is not null or dnd move direction is down  
  const moveDirection = !dropTarget || dropTarget.previousElementSibling === target.nextElementSibling;
  const animationTarget = moveDirection ? 
        (dropTarget ? dropTarget.previousElementSibling : target.nextElementSibling) : target.previousElementSibling;

  // animation
  animationTarget.style.transform = `translateY(${moveDirection ? '-100%' : '100%'})`;
  animationTarget.style.transition = "transform .3s";
  animationTarget.ontransitionend = () => {
    animationTarget.style.transition = "";
    animationTarget.style.transform = "";
    target.parentNode.insertBefore(target, dropTarget);
  };
}

function listItemDragged(e) {
  e.target.classList.add("dragging");
  let dropTarget =
    document.elementFromPoint(e.clientX, e.clientY) === null
      ? e.target
      : document.elementFromPoint(e.clientX, e.clientY);

  if (e.target.parentNode === dropTarget.parentNode) {
    dropTarget =
      dropTarget !== e.target.nextElementSibling ? dropTarget : dropTarget.nextElementSibling;
    if (e.target !== dropTarget) {
      // move target with animation
      moveWithAnimation(e.target, dropTarget);
    }
  }
}

function listItemDropped(e) {
  e.target.classList.remove("dragging");
  e.preventDefault();
}

function onLoad() {
  let listItems = document.querySelectorAll(".draggable");
  Array.prototype.map.call(listItems, (option) => {
    option.ondrag = listItemDragged;
    option.ondragend = listItemDropped;
  });
  document
    .querySelector(".sortable-list")
    .addEventListener("dragover", (e) => e.preventDefault());
}

onLoad();
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Roboto", sans-serif;
}

body {
  background-color: #2b3035;
}

.draggable {
  display: flex;
  padding: 10px 12px;
  border-radius: 5px;
  border: 1px solid #5c636a;
  margin-right: 5px;
  background-color: #212529;
  cursor: grab;
  color: #ffffff;
  touch-action: none;
}

.dragging {
  cursor: grabbing;
  background: transparent;
  color: transparent;
  border: none;
}
<ul class='sortable-list list-unstyled'>
  <li class='draggable' draggable='true'>
    Lorem ipsum dolor sit amet 1
  </li>
  <li class='draggable' draggable='true'>
    Lorem ipsum dolor sit amet 2
  </li>
  <li class='draggable' draggable='true'>
    Lorem ipsum dolor sit amet 3
  </li>
  <li class='draggable' draggable='true'>
    Lorem ipsum dolor sit amet 4
  </li>
</ul>
Cody Chang
  • 507
  • 3
  • 7
  • I changed the transition delay to 0.5 instead of 0.3, there's a few [issues](https://i.stack.imgur.com/SDgab.gif) when hovering happens in the middle of an item, it keeps sliding up and down indefinitely. Also, sometimes, the 2nd item when dragged, the 1st is dragged as well. – nlblack323 Apr 11 '23 at 19:06
  • You can introduce a boolean variable named `isTransitioning` to prevent the animation from being triggered indefinitely. – Cody Chang Apr 12 '23 at 01:40