javascripthtmlcssreactjsreact-beautiful-dnd

Is there is way to change default translucent image by browser when onDragStart event fire


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

screen shoot of my app

image 2 from react-beautiful-dnd screen shot of react-beautiful-dnd


Solution

  • 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>