Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Drag Layer elements don't update until AFTER I drag it. #3615

Open
EOMedvis opened this issue Jan 19, 2024 · 1 comment
Open

Custom Drag Layer elements don't update until AFTER I drag it. #3615

EOMedvis opened this issue Jan 19, 2024 · 1 comment

Comments

@EOMedvis
Copy link

I'm new to React development. As shown below I have a draggable object with a custom drag layer used as its preview (IconPreview) because normal previews don't work on mobile. The preview takes in a string to load an image. I'm running into a problem where if I have multiple draggables on the screen at once, the image doesn't update on the first drag, and it will use the image for the last object I dragged until I drag and let go, then it updates.

I've asked Chat GPT for what the issues is, and its telling me that Custom Drag Layers don't always update immediately due to "performance issues" and that its not fixable without another package. I'm not sure if its BSing me or not. Am I doing something wrong or is this a React DnD issue?

Code:

import { CSSProperties, FC, useState, useEffect } from 'react';
import { useDrag, useDragLayer } from 'react-dnd';
import { DraggableTypes } from './DraggableTypes';
import { getEmptyImage } from "react-dnd-html5-backend";

const iconWidth: number = 48;
const iconHeight: number = 64;

const styleIconNormal: CSSProperties = 
{
  position: 'absolute',
  width: iconWidth + "px",
  height: iconHeight + "px",
  cursor: 'move',
  backgroundImage: 'linear-gradient(to top, #396BE5, #ACA9E4)', 
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  border: '2px solid white',
  borderRadius: '5px',
};

const styleIconImage: CSSProperties =
{
  position:'relative',  
  width: '32px',
  height: '32px',
}

export interface IconProps 
{
  iconData: IconType
  SetInfo(info: {title: string, desc: string, image: string, ranked: boolean, discarded: boolean}): void;  
  feels: boolean;
  SetIconToGlow(icon: string): void;
  SetClicked(clicked: boolean): void;
}

interface IconPreviewProps
{
  imageName: string;
}

//custom drag layer used for a custom preview.
const IconPreview: React.FC<IconPreviewProps> = ({ imageName }) => 
{
  const { isDragging, currentOffset } = useDragLayer((monitor) => 
  ({
    isDragging: monitor.isDragging(),
    currentOffset: monitor.getClientOffset()
  }));  

  if (!isDragging) 
  {
    return null;
  }

  return (
    <div style=
      {{
        ...styleIconNormal, 
        position: 'fixed', 
        pointerEvents: 'none', 
        zIndex: 100, 
        left: (currentOffset?.x || 0) - iconWidth/2, 
        top: (currentOffset?.y || 0) - iconHeight/2,
      }}>      
      <img src={"icon-" + imageName + ".png"} alt="Icon" style={styleIconImage}/>
    </div>
  );
};

//main code for the drop icons that is created when a card is dropped over a rating zone. Props must be listed individually for this function, for some reason, otherwise using iconData as a part of props.iconData breaks the redrag of the 
export const DropIcon: FC<IconProps> = function SetIcon({ iconData, SetInfo, feels, SetIconToGlow, SetClicked}) 
{ 
  const [{ isDragging }, drag, preview] = useDrag
  (() => 
  ({
    type: DraggableTypes.Icon,
    item: {iconData},
    collect: (monitor) => 
      ({
        isDragging: monitor.isDragging()
      }),
      previewOptions: { captureDraggingState: true,} 
    }),
    [iconData]
  );

  useEffect(() => 
  {
    preview(getEmptyImage(), { captureDraggingState: true })
  }, [iconData]);

  const opacity = isDragging ? 0.4 : 1;
  var boxShadow = '';  

  //on mouse down function. Tells Dropzones to change this cards data to glow and fills in the info box.
  function MouseDown()
  {
    if(feels)   
    {     
      SetIconToGlow(iconData.title);
      SetInfo({title: iconData.title, desc: iconData.desc, image: iconData.image, ranked: iconData.ranked, discarded: iconData.discarded});
      SetClicked(true); 
    }       
  }  

  //if in emotion ranking mode, and this card is set to glow, use the glow effect.
  if(iconData.glow && feels)
    boxShadow = '0px 0px 40px 20px #ffffff';  

  return (
    <>
    <IconPreview imageName={iconData.image}/>
    <button ref={drag} 
      onMouseDown={MouseDown} 
      style= {{ ...styleIconNormal, opacity, boxShadow, left: iconData.left + '%', top: iconData.top + '%' }}
    >
      <img src={"icon-" + iconData.image + ".png"} alt="Icon" style={styleIconImage}/>
    </button>   
    </> 
  );
};
@EOMedvis
Copy link
Author

Problem fixed. I wasn't using the userDragLayer monitor to check if the item image changed. Doing the following fixed it:

interface IconPreviewProps
{
imageName: string;
isDragging: boolean;
}

//custom drag layer used for a custom preview.
const IconPreview: FC = (props) =>
{
const {imageName, isDragging, currentOffset } = useDragLayer((monitor) =>
({
imageName: props.imageName,
isDragging: props.isDragging,
currentOffset: monitor.getClientOffset()
}));

console.log("item: " + props.imageName);

if (!isDragging)
{
return null;
}

return (
<div style=
{{
...styleIconNormal,
position: 'fixed',
pointerEvents: 'none',
zIndex: 100,
left: (currentOffset?.x || 0) - iconWidth/2,
top: (currentOffset?.y || 0) - iconHeight/2,
}}>
<img src={"icon-" + imageName + ".png"} alt="Icon" style={styleIconImage}/>

);
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant