next/image 비율에 상관없이 container에 꽉차게 width, height 100% 주기

<div>elop·2023년 3월 8일
2

사건의 발.단.

react-slick으로 리뷰이미지들에 대한 slide를 만들었다.

리뷰이미지여서 위와 같이 , label 딱지가 있는 이미지 slide다. 구현조건은 아래와 같다.

  1. 가로로 긴 이미지는 위와 같이 가로로 꽉 차야한다.
  2. 세로로 긴 이미지는 반대로 세로로 꽉 차야한다.
  3. 이미지 비율은 유지되어야 한다. (당연함ㅋ)
  4. 이미지와 딱 맞는 div의 position을 relative로 주고 label position을 absolute로 줘야한다.
  5. 사용자 device의 가로, 세로 비율과 상관없이 동작해야한다. (얘도 당연함ㅋ)

문제점

하지만 next/image 이 녀석 역시 호락호락하지 않다. (사실 얘 때매 빡친적 수두루빽빽임ㅋ)
일단 next/image에서 이미지의 비율을 유지하면서 container에 꽉차게 하려면

  1. layout은 fill, objectFit은 contain
  2. image의 부모 div의 width, height은 100%

을 줘야하는데, 문제는 가로와 세로중 어디가 꽉차야 할지 모르는 상황이라는 것이다. div의 width와 heigh이 모두 100%가 되면 아래처럼 , 라벨을 올바르게 줄수 없다.

(되는데 내가 멍청한걸 수도 있음ㅋ)

해결

그래서 next/image의 onLoad로 image의 width와 height을 가져오고, 상황에 따라 width와 height을 다르게 줬다.

또 사용자가 웹으로 보는지, 모바일로 보는지 모르기 때문에 아래와 같은 로직을 추가했다.

  1. 사용자 device의 width, height 비율과 이미지의 width, height비율을 비교
  2. 이미지쪽 비율이 더 넓으면 width를 100%, 반대의 경우 height을 100%
const styles = {  
  imageWrapper: (isFullWidth: boolean) => ({
    position: "relative",
    ...(isFullWidth
          // 이미지가 가로로 꽉찰 경우 width를 100%
      ? { width: "100% !important" }
      : {
          width: "auto !important",
      	  // 이미지가 세로로 꽉찰 경우 차지해야할 height
          height: "calc(100vh - ((4.5rem) / 1.6) - ((7rem) / 1.6))", 
        }),
    "& > img": {
      position: "relative !important",
      objectFit: "contain",
      maxWidth: "100%",
      maxHeight: "calc(100vh - ((4.5rem) / 1.6) - ((7rem) / 1.6)) !important",
    },
  }),
  label: {
    position: "absolute",
    top: 0,
    left: 0,
    ...
  },
};

...

export default function ImageWidthLabel() {
  ...
  
  const [imageWidth, setImageWidth] = useState(0);
  const [imageHeight, setImageHeight] = useState(0);

  const reviewImageOnload = ({target}:React.SyntheticEvent<HTMLImageElement>) => {
    const { naturalWidth, naturalHeight } = target as HTMLImageElement;
    setImageWidth(naturalWidth);
    setImageHeight(naturalHeight);
  };

  // 800px : 웹의 maxWidth
  const isFullWidth = imageWidth / imageHeight >= Math.min(window.innerWidth, 800) / window.innerHeight;

  return (
    <Box sx={styles.imageWrapper(isFullWidth)}>
      <ExternalImage src={link} onLoad={reviewImageOnload} />
      <Box sx={styles.label}>{type === 0 ? "전" : "후"}</Box>
    </Box>
  );
}

결과

잘 동작하지만 뭔가 빙빙 돌아온 느낌..
qa가 들어와서 급하게 로직으로 해결했지만 css만으로도 해결할 수 있을 것 같은 아쉬움이 든다. 그냥 css로 구현하는법 아시는 분 공유바랍니다..!


profile
기록장

0개의 댓글