I hope you're all doing well, and I appreciate your attempts to help. What I'm trying to do here is practice and improve my skills, so I decided to create a simple drag-and-drop function for an existing list. I'm using the react-beautiful-dnd library as a reference. However, I'm currently facing an issue with the translucent image that the browser generates by default during the drag event. I can only hide this image, but when looking at the react-beautiful-dnd library, the full element appears without any effects. I still don't know how to display the full element, as they do in the Beautiful Drag & Drop library. Can you help me with this?
I will include two images to illustrate what I mean. In my image, you can see that the transparency of the dragged element changes, unlike what happens in the Beautiful DnD library, as shown in the second image.
image 1 from my code
Anyway, I decided to go with the solution of creating a copy of the element and making it follow the mouse movement while hiding the original element to create the illusion that the element is being dragged. I also improved the animation by using requestAnimationFrame. This is the code I came up with; maybe it will help someone else solve the problem.
export const VideosELEments = () => {
const [sections, setSections] = useState(DummSections());
const [draggedSection, setDraggedSection] = useState<number | null>(null);
const sectionRefs = useRef<(HTMLDivElement | null)[]>([]);
const clonedElementRef = useRef<HTMLDivElement | null>(null);
const animationFrameRef = useRef<number | null>(null);
const offsetXRef = useRef(0);
const offsetYRef = useRef(0);
const handleDragStart = (index: number) => (e: React.DragEvent<HTMLDivElement>) => {
setDraggedSection(index);
if (e.dataTransfer) {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', index.toString());
e.dataTransfer.setDragImage(new Image(), 0, 0);
}
const currentElement = e.currentTarget;
const cloned = currentElement.cloneNode(true) as HTMLDivElement;
cloned.style.position = 'fixed';
cloned.style.pointerEvents = 'none';
cloned.style.opacity = '1';
cloned.style.zIndex = '1000';
document.body.appendChild(cloned);
clonedElementRef.current = cloned;
const rect = currentElement.getBoundingClientRect();
offsetXRef.current = e.clientX - rect.left;
offsetYRef.current = e.clientY - rect.top;
const updateClonedElementPosition = (moveEvent: MouseEvent) => {
if (clonedElementRef.current) {
animationFrameRef.current = requestAnimationFrame(() => {
if (clonedElementRef.current) {
clonedElementRef.current.style.left = `${moveEvent.clientX - offsetXRef.current}px`;
clonedElementRef.current.style.top = `${moveEvent.clientY - offsetYRef.current}px`;
}
});
}
};
document.addEventListener('mousemove', updateClonedElementPosition);
e.currentTarget.addEventListener('dragend', () => {
document.removeEventListener('mousemove', updateClonedElementPosition);
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
if (clonedElementRef.current) {
document.body.removeChild(clonedElementRef.current);
clonedElementRef.current = null;
}
}, { once: true });
};
const handleDragOver = (index: number) => (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
if (draggedSection === null || draggedSection === index) return;
const newSections = [...sections];
const draggedItem = newSections[draggedSection];
newSections.splice(draggedSection, 1);
newSections.splice(index, 0, draggedItem);
setSections(newSections);
setDraggedSection(index);
};
const handleDragEnd = () => {
setDraggedSection(null);
};
const handlMainContainerMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (clonedElementRef.current) {
animationFrameRef.current = requestAnimationFrame(() => {
if (clonedElementRef.current) {
const x = e.clientX - offsetXRef.current;
const y = e.clientY - offsetYRef.current;
clonedElementRef.current.style.left = `${x}px`;
clonedElementRef.current.style.top = `${y}px`;
}
});
}
};
useEffect(() => {
sectionRefs.current.forEach((ref, index) => {
if (ref) {
setTimeout(() => {
ref.classList.add('fade-in-up');
}, index * 100);
}
});
return () => {
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
};
}, []);
return (
<div className='w-full h-full p-5 relative'>
<div className='bg-cyan-400 w-fit flex flex-col gap-3 relative p-3 rounded overflow-hidden' onMouseMove={(e) => handlMainContainerMove(e)} onDragOver={(e) => handlMainContainerMove(e)}>
{sections.map((sec, idx) => (
<div
key={sec.id}
ref={(el) => { (sectionRefs.current[idx] = el) }}
draggable
onDragStart={handleDragStart(idx)}
onDragOver={(e) => handleDragOver(idx)(e)}
onDragEnd={handleDragEnd}
className={`
w-72 opacity-0 hover:cursor-grab bg-slate-50 shadow rounded-md p-2
transition-all duration-300 ease-out
${draggedSection === idx ? 'opacity-0' : 'opacity-100'}
`}
>
<div className='flex flex-wrap flex-col gap-2'>
<div className='w-5 shadow bg-orange-500 rounded'>
<p className='text-center text-white'>{idx + 1}</p>
</div>
<p className='text-gray-500 font-thin'>الفصل</p>
<div className='flex justify-between items-center'>
<p className='font-bold text-xl'>{sec.title}</p>
<div>
<Button variant='bordered' color="primary" onClick={() => { }}>
<p> + إضافه فيديو</p>
</Button>
</div>
</div>
</div>
</div>
))}
</div>
</div>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>