Home > Net >  How to dynamically set background image to card component from an array?
How to dynamically set background image to card component from an array?

Time:07-28

Not sure exactly how to describe this. I have a grid of cards each displaying a different game and I'd like to set each background-image to an image of the game. Here is the code I have so far:

const games = [
  {
    id: 1,
    background: '../images/game1.png',
    name: 'Game 1',
    description:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vel sapien arcu. Donec sapien eros, efficitur blandit dui vitae, imperdiet consectetur nibh.',
  },
  {
    id: 2,
    background: '../images/game2.png',
    name: 'Game 2',
    description:
      'Nunc efficitur tincidunt malesuada. Pellentesque blandit sapien sed orci tristique molestie. Donec ut metus a sapien gravida convallis sed maximus neque. Aliquam consequat fringilla porta. Curabitur eget semper tortor.',
  },
];

export const GameCard = () => {
  const [isOpen, setIsOpen] = useState(null);

  return (
    <div id="card_wrapper">
      {games.map((game) => (
        <motion.div
          transition={{ layout: { duration: 1, type: 'spring' } }}
          layout
          onClick={() => setIsOpen(!isOpen)}
          key={game.id}
          
          style={{
            backgroundImage: `url(${game.background}) no-repeat`,
          }}
        >
          <motion.h2 layout="position"> {game.name} </motion.h2>
          {isOpen && (
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              transition={{ duration: 1 }}
              
            >
              <p> {game.description} </p>
            </motion.div>
          )}
        </motion.div>
      ))}
    </div>
  );
};

Specifically

        <motion.div
          transition={{ layout: { duration: 1, type: 'spring' } }}
          layout
          onClick={() => setIsOpen(!isOpen)}
          key={game.id}
          
          style={{
            backgroundImage: `url(${game.background}) no-repeat`,
          }}
        >

is where I'm trying to pass the image as a background but it doesn't work.

Edit showing console : enter image description here

And save your backgrounds there. This is an example from one of my projects. In my case, I would search for the background images on /assets/background/background.png.

Using this option (with my folder structure as example), your array would be the following, and you won't have to change the code in your component:

const games = [
  {
    id: 1,
    background: '/assets/background/game1.png',
    name: 'Game 1',
    description:
      'Lorem ipsum ...',
  }
];

2th option

Set that property on your CSS. When you set background-image: url(../images/game1.png) in the style sheet, it will work, because when you set it, the component hasn't been rendered yet. So when it is rendered, the actual URL will be one created by the compiler which will be able to find the image. In order to do that, you would have to create a class, or something like that, for each of the games:

.game1{
    background-image: url('../images/game1.png'),
}

<motion.div
    transition={{ layout: { duration: 1, type: 'spring' } }}
    layout
    onClick={() => setIsOpen(!isOpen) && renderImgArr}
    key={game.id}
    
>

Final considerations: I would use the first option. It is much cleaner and will save you to write and apply a lot of custom classes.

CodePudding user response:

Actually your onClick handler just handle open and close, you need define background img first as state on empty array before rendering on jsx, like so

export const GameCard = () => {
const [isOpen, setIsOpen] = useState(null);
const [imgList, setImgList] = useState([]);
const imgRef = useRef()
const renderImgArr = (dataImg) => {
    setImgList([dataImg])
    /* ....define imgRef ...*/
}

return (
    <div id="card_wrapper">
      {games.map((game) => (
        <motion.div
          transition={{ layout: { duration: 1, type: 'spring' } }}
          layout
          // add renderImg and also possible to add setTimeOut on renderImgArr 
          onClick={() => setIsOpen(!isOpen) && renderImgArr}
          key={game.id}
          
          style={{
            backgroundImage: `url(${game.background}) no-repeat`,
          }}
        >
          <motion.h2 layout="position"> {game.name} </motion.h2>

         {/* render imgList with isOpen */}
          {isOpen && imgList && ( 
            <motion.div
              initial={{ opacity: 0 }}
             // define ref as a target reference 
              animate={imageRef.current.target, { opacity: 1 }}
              transition={{ duration: 1 }}
              
            >
              <p> {game.description} </p>
            </motion.div>
          )}
        </motion.div>
      ))}
    </div>
  );
};

This not perfectly work but you can have imagine how it's work, hopefully :)

  • Related