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.
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.
You can create a smooth animation effect using CSS transition
while reordering elements in a list with drag and drop functionality.
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:
var windowClientX = 0, windowClientY = 0;
window.addEventListener('dragover', (event) => {
windowClientX = event.clientX;
windowClientY = event.clientY;
});
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) {
var clientX = e.clientX || windowClientX;
var clientY = e.clientY || windowClientY;
e.target.classList.add("dragging");
let dropTarget =
document.elementFromPoint(clientX, windowClientY) === null
? e.target
: document.elementFromPoint(clientX, windowClientY);
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>